using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.Core;
namespace Tango.PPC.Jobs.Models
{
public class SegmentsGroupModel : ExtendedObject, ISegmentModel
{
#region Properties
public SynchronizedObservableCollection<SegmentModel> Segments { get; set; }
private Int32 _segnmentIndex;
public Int32 SegmentIndex
{
get
{
return _segnmentIndex;
}
set
{
if(_segnmentIndex != value)
{
_segnmentIndex = value;
RaisePropertyChangedAuto();
}
}
}
private int _repeats;
public int Repeats
{
get { return _repeats; }
set { _repeats = value;
RaisePropertyChangedAuto();
RaisePropertyChanged(nameof(Length));
}
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value;
RaisePropertyChangedAuto();
}
}
public JobModel Job { get; set; }
protected Double _length;
/// <summary>
/// Gets or sets the length.
/// </summary>
public Double Length
{
get
{
return Segments.Count == 0 ? 0<using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.Core;
using Tango.PPC.Common.Application;
using Tango.PPC.Common.BackupRestore;
using Tango.Core.ExtensionMethods;
using Tango.Core.DI;
using Tango.PPC.Common.Connection;
using System.IO;
using Tango.Core.Helpers;
using Tango.BL;
using Tango.Settings;
using Tango.Core.DB;
using System.Data.SqlClient;
using Ionic.Zip;
using Tango.BL.Entities;
using Tango.PPC.Common.Authentication;
using Tango.Integration.Upgrade;
using Tango.Core.IO;
namespace Tango.PPC.Common.BackupRestore
{
public class DefaultBackupManager : ExtendedObject, IBackupManager
{
private const string BACKUP_FILE_NAME = "Backup.json";
private const string DATABASE_FILE_NAME = "Tango.bak";
private const int VERSION = 1;
private const string PASSWORD = "1Creativity";
[TangoInject(TangoInjectMode.WhenAvailable)]
private IPPCApplicationManager _applicationManager;
[TangoInject(TangoInjectMode.WhenAvailable)]
private IMachineProvider _machineProvider;
[TangoInject(TangoInjectMode.WhenAvailable)]
private IAuthenticationProvider _authenticationProvider;
public DefaultBackupManager()
{
TangoIOC.Default.Inject(this);
}
public event EventHandler<BackupRestoreProgressEventArgs> Progress;
public Task CreateBackup(string filePath, String name, BackupSettings settings)
{
return Task.Factory.StartNew(() =>
{
var tempFolder = TemporaryManager.CreateFolder();
try
{
//Basic
LogManager.Log($"Starting backup operation to file '{filePath}'...");
LogManager.Log($"Backup settings:\n{settings.ToJsonString()}");
OnProgress(BackupRestoreStage.Initializing);
LogManager.Log($"Temporary folder created on {tempFolder.Path}.");
BackupFile backupFile = new BackupFile();
backupFile.Version = VERSION;
backupFile.Date = DateTime.Now;
backupFile.Settings = settings;
backupFile.Name = name;
backupFile.MachineSerialNumber = _machineProvider.Machine.SerialNumber;
backupFile.ApplicationVersion = _applicationManager.Version.ToString();
//Firmware
try
{
LogManager.Log("Extracting firmware version from local tfp package...");
using (var st = File.OpenRead(Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "firmware_package.tfp")))
{
backupFile.FirmwareVersion = _machineProvider.MachineOperator.GetFirmwarePackageInfo(st).Result.FileDescriptors.SingleOrDefault(x => x.Destination == PMR.FirmwareUpgrade.VersionFileDestination.Mcu).Version;
}
}
catch (Exception ex)
{
throw new FileLoadException("Could extract the firmware version from the TFP package.", ex);
}
LogManager.Log($"Backup file generated:\n{backupFile.ToJsonString()}");
if (settings.Mode == BackupMode.Jobs)
{
//Jobs
LogManager.Log("Starting jobs backup...");
OnProgress(BackupRestoreStage.BackingupJobs);
using (ObservablesContext db = ObservablesContext.CreateDefault())
{
var jobs = db.Jobs.ToList();
foreach (var job in jobs)
{
try
{
LogManager.Log($"Backing up job '{job.Name}'...");
var jobFile = job.ToJobFile().Result;
backupFile.JobFiles.Add(jobFile);
OnProgress(BackupRestoreStage.BackingupJobs, jobs.IndexOf(job) + 1, jobs.Count, false);
}
catch (Exception ex)
{
throw new InvalidOperationException($"Error extracting job {job.Name}.", ex);
}
}
}
LogManager.Log("Jobs backup completed.");
}
else
{
//User Settings
LogManager.Log("Backing up application settings...");
OnProgress(BackupRestoreStage.BackingupSettings);
backupFile.SettingsFile = File.ReadAllText(SettingsManager.Default.FilePath);
//Application Version
LogManager.Log("Backing up application files...");
OnProgress(BackupRestoreStage.BackingupApplication);
try
{
PathHelper.CopyDirectory(AssemblyHelper.GetCurrentAssemblyFolder(), tempFolder, true, (current, total) =>
{
OnProgress(BackupRestoreStage.BackingupApplication, current, total, false);
});
}
catch (Exception ex)
{
throw new IOException($"Error occurred while copying application files.", ex);
}
//Database
LogManager.Log("Backing up database...");
OnProgress(BackupRestoreStage.BackingupDatabase);
try
{
var dataSource = ObservablesContext.GetActualDataSource();
using (var dbManager = DbManager.FromDataSource(dataSource))
{
Directory.CreateDirectory("C:\\Backups");
var dbBackupFile = $"C:\\Backups\\{DATABASE_FILE_NAME}";
if (File.Exists(dbBackupFile))
{
File.Delete(dbBackupFile);
}
dbManager.Backup(dataSource.Catalog, dbBackupFile);
File.Move(dbBackupFile, Path.Combine(tempFolder, DATABASE_FILE_NAME));
}
}
catch (Exception ex)
{
throw new IOException("Error creating database backup", ex);
}
LogManager.Log("Database backup completed.");
}
//Backup.json
try
{
OnProgress(BackupRestoreStage.WritingConfiguration);
var backupFilePath = Path.Combine(tempFolder, BACKUP_FILE_NAME);
LogManager.Log($"Writing backup configuration file '{backupFilePath}'...");
File.WriteAllText(backupFilePath, backupFile.ToJsonString());
}
catch (Exception ex)
{
throw new IOException("Error writing backup configuration file.", ex);
}
//Compression
LogManager.Log($"Generating {filePath}...");
using (ZipFile zip = new ZipFile())
{
zip.Password = PASSWORD;
zip.AddDirectory(tempFolder);
zip.SaveProgress += (x, e) =>
{
if (e.EventType == ZipProgressEventType.Saving_AfterWriteEntry)
{
LogManager.Log($"Compressing '{e.CurrentEntry.FileName}'...");
OnProgress(BackupRestoreStage.CompressingFiles, e.EntriesSaved + 1, e.EntriesTotal, false);
}
};
zip.ParallelDeflateThreshold = -1;
zip.Save(filePath);
}
//Done
LogManager.Log("Backup operation completed!!!");
OnProgress(BackupRestoreStage.Done, 100, 100, false);
tempFolder.Delete();
}
catch (Exception ex)
{
tempFolder.Delete();
OnProgress(BackupRestoreStage.Error, 100, 100, false);
LogManager.Log(ex, "Could not complete the backup operation.");
throw ex;
}
});
}
/// <summary>
/// Extracts the backup configuration from the specified backup file.
/// </summary>
/// <param name="filePath">The file path.</param>
/// <returns></returns>
public Task<BackupFile> ExtractBackupConfiguration(string filePath)
{
return Task.Factory.StartNew<BackupFile>(() =>
{
using (ZipFile zip = ZipFile.Read(filePath))
{
zip.Password = PASSWORD;
var reader = zip.Entries.SingleOrDefault(x => x.FileName == BACKUP_FILE_NAME).OpenReader();
String json = String.Empty;
using (StreamReader stReader = new StreamReader(reader))
{
json = stReader.ReadToEnd();
}
var backupFile = BackupFile.FromJson(json);
reader.Close();
reader.Dispose();
return backupFile;
}
});
}
public Task<RestoreResult> Restore(string filePath, RestoreSettings settings)
{
TaskCompletionSource<RestoreResult> completionSource = new TaskCompletionSource<RestoreResult>();
String dbRollbackFile = null;
bool shouldRollback = false;
Task.Factory.StartNew(() =>
{
LogManager.Log($"Starting restore operation from file '{filePath}'...");
OnProgress(BackupRestoreStage.Initializing);
var tempFolder = TemporaryManager.CreateFolder();
tempFolder.Persist = true;
var restoreResult = new RestoreResult() { FolderPath = tempFolder };
try
{
LogManager.Log("Extracting backup file configuration...");
BackupFile backupFile = null;
//Extract Configuration
try
{
OnProgress(BackupRestoreStage.ExtractingBackupConfiguration);
backupFile = ExtractBackupConfiguration(filePath).Result;
restoreResult.BackupFile = backupFile;
LogManager.Log($"Backup settings:\n{backupFile.Settings.ToJsonString()}");
}
catch (Exception ex)
{
throw new IOException("Error extracting backup configuration.", ex);
}
//Validate Version
if (backupFile.Version > VERSION)
{
throw new NotSupportedException($"Backup file version {backupFile} is not supported.");
}
//Validate Machine Serial Number
if (backupFile.Settings.Mode == BackupMode.Full && backupFile.MachineSerialNumber != _machineProvider.Machine.SerialNumber)
{
throw new InvalidOperationException($"The specified backup file targets machine '{backupFile.MachineSerialNumber}'. Cannot perform the restore operation.");
}
//Validate Machine State
LogManager.Log("Validating machine state...");
OnProgress(BackupRestoreStage.ValidatingMachineState);
if (_machineProvider.MachineOperator.IsPrinting)
{
LogManager.Log("The machine is currently printing. Aborting!");
throw new InvalidOperationException("Cannot perform restore operation while machine is dyeing.");
}
if (backupFile.Settings.Mode == BackupMode.Full && _machineProvider.MachineOperator.State != Transport.TransportComponentState.Connected)
{
LogManager.Log("Backup is configured to restore the firmware but machine is not connected!");
throw new InvalidOperationException("The restore operation is configured to restore the firmware version but the machine is currently disconnected.");
}
//Create Restore Point
try
{
LogManager.Log("Creating database rollback file...");
var dataSource = ObservablesContext.GetActualDataSource();
using (var dbManager = DbManager.FromDataSource(dataSource))
{
Directory.CreateDirectory("C:\\Backups");
dbRollbackFile = $"C:\\Backups\\{Path.GetRandomFileName()}.bak";
LogManager.Log($"Creating database rollback to '{dbRollbackFile}'...");
dbManager.Backup(dataSource.Catalog, dbRollbackFile);
LogManager.Log("Database rollback created successfully.");
shouldRollback = true;
}
}
catch (Exception ex)
{
throw new InvalidDataException("Error creating database rollback file.", ex);
}
if (backupFile.Settings.Mode == BackupMode.Jobs)
{
//Restore Jobs
OnProgress(BackupRestoreStage.RestoringJobs);
LogManager.Log("Starting jobs restore...");
using (ObservablesContext db = ObservablesContext.CreateDefault())
{
var jobs = db.Jobs.ToList();
var jobFiles = backupFile.JobFiles;
if (settings.AllowDeleteJobs)
{
try
{
LogManager.Log("Removing existing jobs...");
foreach (var job in jobs.ToList())
{
LogManager.Log($"Removing job '{job.Name}'...");
job.Delete(db);
jobs.Remove(job);
}
db.SaveChanges();
}
catch (Exception ex)
{
throw new Exception("Error removing existing jobs from database.", ex);
}
}
foreach (var jobFile in jobFiles)
{
LogManager.Log($"Importing job '{jobFile.Name}'...");
try
{
var existingJob = jobs.FirstOrDefault(x => x.Name == jobFile.Name);
if (existingJob != null)
{
if (settings.OverwriteExistingJobs)
{
try
{
LogManager.Log("Job already exist, overwriting...");
var newJob = Job.FromJobFile(jobFile, _machineProvider.Machine.Guid, _authenticationProvider.CurrentUser.Guid).Result;
newJob.Guid = existingJob.Guid;
existingJob.Delete(db);
jobs.Remove(existingJob);
db.SaveChanges();
db.Jobs.Add(newJob);
db.SaveChanges();
}
catch (Exception ex)
{
throw new InvalidOperationException("Error overwriting job.", ex);
}
}
}
else
{
var newJob = Job.FromJobFile(jobFile, _machineProvider.Machine.Guid, _authenticationProvider.CurrentUser.Guid).Result;
db.Jobs.Add(newJob);
}
}
catch (Exception ex)
{
throw new InvalidOperationException("Error importing job.", ex);
}
OnProgress(BackupRestoreStage.RestoringJobs, jobFiles.IndexOf(jobFile) + 1, jobFiles.Count, false);
}
OnProgress(BackupRestoreStage.RestoringJobs);
db.SaveChanges();
}
LogManager.Log("Jobs restored successfully.");
OnProgress(BackupRestoreStage.Done, 100, 100, false);
completionSource.SetResult(restoreResult);
}
else
{
//Extract zip file
LogManager.Log("Starting backup file extraction...");
OnProgress(BackupRestoreStage.ExtractingContent);
try
{
using (ZipFile zip = new ZipFile(filePath))
{
zip.Password = PASSWORD;
zip.ExtractProgress += (x, e) =>
{
if (e.EventType == ZipProgressEventType.Extracting_AfterExtractEntry)
{
LogManager.Log($"Extracting '{e.CurrentEntry.FileName}'...");
OnProgress(BackupRestoreStage.ExtractingContent, e.EntriesExtracted + 1, e.EntriesTotal, false);
}
};
zip.ParallelDeflateThreshold = -1;
zip.ExtractAll(tempFolder);
}
}
catch (Exception ex)
{
throw new IOException("Error extracting backup content.", ex);
}
//Overwrite settings
LogManager.Log("Validating user settings...");
if (backupFile.SettingsFile != null)
{
try
{
LogManager.Log("Overwriting settings file...");
OnProgress(BackupRestoreStage.RestoringSettings);
File.WriteAllText(SettingsManager.Default.FilePath, backupFile.SettingsFile);
}
catch (Exception ex)
{
throw new IOException("Error overwriting user settings.", ex);
}
}
else
{
LogManager.Log("No user settings, skipping...");
}
//Restore database
var backupFilePath = Path.Combine(tempFolder, DATABASE_FILE_NAME);
LogManager.Log($"Looking for file database backup on '{backupFilePath}'...");
if (File.Exists(backupFilePath))
{
LogManager.Log("Restoring database...");
OnProgress(BackupRestoreStage.RestoringDatabase);
try
{
var dataSource = ObservablesContext.GetActualDataSource();
using (var dbManager = DbManager.FromDataSource(dataSource))
{
Directory.CreateDirectory("C:\\Backups");
var dbBackupFile = $"C:\\Backups\\{DATABASE_FILE_NAME}";
File.Copy(backupFilePath, dbBackupFile, true);
dbManager.Restore(dataSource.Catalog, dbBackupFile);
File.Delete(dbBackupFile);
}
}
catch (Exception ex)
{
throw new IOException("Error restoring database backup", ex);
}
LogManager.Log("Database backup completed.");
}
else
{
LogManager.Log("Database backup file not found, skipping...");
}
//Remove extra files from application temp folder
OnProgress(BackupRestoreStage.RemovingTemporaryFiles);
LogManager.Log("Removing redundant files from temp folder...");
try
{
File.Delete(backupFilePath);
}
catch { }
try
{
File.Delete(Path.Combine(tempFolder, BACKUP_FILE_NAME));
}
catch { }
//Update firmware
var tfpFile = Path.Combine(tempFolder, "firmware_package.tfp");
LogManager.Log($"Looking for tfp file on '{tfpFile}'...");
if (File.Exists(tfpFile))
{
OnProgress(BackupRestoreStage.RestoringFirmware);
LogManager.Log("Restoring firmware version...");
var stream = new FileStream(tfpFile, FileMode.Open);
if (_machineProvider.Machine.IsDemo)
{
_machineProvider.MachineOperator.FirmwareUpgradeMode = FirmwareUpgradeModes.TFP_PACKAGE;
}
else
{
_machineProvider.MachineOperator.FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE;
}
var handler = _machineProvider.MachineOperator.UpgradeFirmware(stream).Result;
handler.Failed += (_, ex) =>
{
stream.Dispose();
OnRestoreException(ex, completionSource, shouldRollback, dbRollbackFile, tempFolder);
};
handler.Completed += (_, __) =>
{
OnProgress(BackupRestoreStage.RestoringFirmware, 100, 100, false);
stream.Dispose();
LogManager.Log("Full backup restored successfully.");
OnProgress(BackupRestoreStage.Done, 100, 100, false);
completionSource.SetResult(restoreResult);
};
handler.Canceled += (_, __) =>
{
stream.Dispose();
OnRestoreException(new Exception("The operation has been canceled."), completionSource, shouldRollback, dbRollbackFile, tempFolder);
};
handler.Progress += (_, e) =>
{
OnProgress(BackupRestoreStage.RestoringFirmware, e.Current, e.Total, false);
};
}
else
{
LogManager.Log("Firmware package file not found, skipping...");
OnProgress(BackupRestoreStage.Done, 100, 100, false);
completionSource.SetResult(restoreResult);
}
}
}
catch (Exception ex)
{
OnRestoreException(ex, completionSource, shouldRollback, dbRollbackFile, tempFolder);
}
});
return completionSource.Task;
}
private void OnRestoreException(Exception ex, TaskCompletionSource<RestoreResult> completionSource, bool shouldRollback, String dbRollbackFile, TemporaryFolder tempFolder)
{
if (shouldRollback)
{
LogManager.Log("Rolling back database changes...");
var dataSource = ObservablesContext.GetActualDataSource();
using (var dbManager = DbManager.FromDataSource(dataSource))
{
try
{
OnProgress(BackupRestoreStage.RollingBackChanges);
dbManager.Restore(dataSource.Catalog, dbRollbackFile);
LogManager.Log("Database restored successfully.");
}
catch (Exception e)
{
LogManager.Log(e, "Error rolling back database.");
}
finally
{
try
{
File.Delete(dbRollbackFile);
}
catch { }
}
}
}
tempFolder.Delete();
OnProgress(BackupRestoreStage.Error, 100, 100, false);
LogManager.Log(ex, "Could not complete the restore operation.");
completionSource.SetException(ex);
}
protected virtual void OnProgress(BackupRestoreStage stage, double progress = 0, double maxProgress = 100, bool isIntermediate = true)
{
Progress?.Invoke(this, new BackupRestoreProgressEventArgs()
{
Stage = stage,
Progress = progress,
MaxProgress = maxProgress,
IsIntermediate = isIntermediate,
});
}
}
}