aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Controls/RealTimeGraphControl.xaml.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Controls/RealTimeGraphControl.xaml.cs')
0 files changed, 0 insertions, 0 deletions
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using Tango.Core.Commands;
using Tango.Core.Helpers;
using Tango.BL.Entities;
using Tango.MachineStudio.Common.Notifications;
using Tango.SharedUI;
using SimpleValidator.Extensions;
using Tango.MachineStudio.Common.StudioApplication;
using Tango.MachineStudio.Common;
using Tango.BL;
using Tango.AutoComplete.Editors;
using System.Data.Entity;
using Tango.BL.Builders;
using Tango.MachineStudio.MachineDesigner.Contracts;
using System.Windows.Threading;
using Tango.Core.Threading;
using Tango.MachineStudio.RML.ViewModels;
using Tango.Settings;
using Tango.MachineStudio.RML.Models;
using Tango.BL.ActionLogs;
using Tango.MachineStudio.Common.Authentication;
using Tango.BL.DTO;
using Tango.Core.Cryptography;
using Tango.BL.Enumerations;
using Tango.MachineStudio.MachineDesigner.Models;

namespace Tango.MachineStudio.MachineDesigner.ViewModels
{
    public class MainViewVM : StudioViewModel<IMainView>
    {
        private INotificationProvider _notification;
        private IActionLogManager _actionLogManager;
        private IAuthenticationProvider _authentication;
        private ActionTimer _machines_action_timer;
        private ActionTimer _dispensers_action_timer;
        private MachineDTO _machineBeforeSave;
        private List<Site> _all_sites;

        #region Properties

        private bool _isNewMachine;
        public bool IsNewMachine
        {
            get { return _isNewMachine; }
            set { _isNewMachine = value; RaisePropertyChangedAuto(); }
        }

        private ObservablesStaticCollections _activeMachineAdapter;
        public ObservablesStaticCollections ActiveMachineAdapter
        {
            get { return _activeMachineAdapter; }
            set { _activeMachineAdapter = value; RaisePropertyChangedAuto(); }
        }

        private List<MachineModel> _machines;
        /// <summary>
        /// Gets or sets the available filtered machines.
        /// </summary>
        public List<MachineModel> Machines
        {
            get { return _machines; }
            set { _machines = value; RaisePropertyChangedAuto(); }
        }

        private MachineModel _selectedMachine;
        /// <summary>
        /// Gets or sets the selected machine from the drop down.
        /// </summary>
        public MachineModel SelectedMachine
        {
            get { return _selectedMachine; }
            set
            {
                if (_selectedMachine != value)
                {
                    _selectedMachine = value;
                    RaisePropertyChangedAuto();
                    InvalidateRelayCommands();
                }
            }
        }

        private Machine _activeMachine;
        /// <summary>
        /// Gets or sets the active machine.
        /// </summary>
        public Machine ActiveMachine
        {
            get { return _activeMachine; }
            set { _activeMachine = value; RaisePropertyChangedAuto(); }
        }

        private IdsPack _selectedIds;
        /// <summary>
        /// Gets or sets the selected ids pack.
        /// </summary>
        public IdsPack SelectedIds
        {
            get { return _selectedIds; }
            set { _selectedIds = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
        }

        private String _dispensersFilter;
        /// <summary>
        /// Gets or sets the dispensers filter.
        /// </summary>
        public String DispensersFilter
        {
            get { return _dispensersFilter; }
            set { _dispensersFilter = value; RaisePropertyChangedAuto(); OnDispensersFilterChanged(); }
        }

        private String _filter;
        /// <summary>
        /// Gets or sets the machines filter.
        /// </summary>
        public String Filter
        {
            get { return _filter; }
            set { _filter = value; RaisePropertyChangedAuto(); OnFilterChanged(); }
        }

        private Spool _selectedSpool;
        public Spool SelectedSpool
        {
            get { return _selectedSpool; }
            set { _selectedSpool = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
        }

        private Site _selectedSite;
        public Site SelectedSite
        {
            get { return _selectedSite; }
            set { _selectedSite = value; RaisePropertyChangedAuto(); }
        }

        private List<Site> _sites;
        public List<Site> Sites
        {
            get { return _sites; }
            set { _sites = value; RaisePropertyChangedAuto(); }
        }

        private bool _isGen1Machine;
        public bool IsGen1Machine
        {
            get { return _isGen1Machine; }
            set { _isGen1Machine = value; RaisePropertyChangedAuto(); }
        }

        private ColorCalibrationViewVM _colorCalibrationViewVM;
        public ColorCalibrationViewVM ColorCalibrationViewVM
        {
            get { return _colorCalibrationViewVM; }
            set { _colorCalibrationViewVM = value; RaisePropertyChangedAuto(); }
        }

        private HardwareConfigurationViewVM _hardwareConfigurationViewVM;
        public HardwareConfigurationViewVM HardwareConfigurationViewVM
        {
            get { return _hardwareConfigurationViewVM; }
            set
            {
                _hardwareConfigurationViewVM = value;
                RaisePropertyChangedAuto();
            }
        }

        private MachineUpdatesViewVM _machineUpdatesViewVM;
        public MachineUpdatesViewVM MachineUpdatesViewVM
        {
            get { return _machineUpdatesViewVM; }
            set { _machineUpdatesViewVM = value; RaisePropertyChangedAuto(); }
        }

        private TupViewVM _tupViewVM;
        public TupViewVM TupViewVM
        {
            get { return _tupViewVM; }
            set { _tupViewVM = value; RaisePropertyChangedAuto(); }
        }

        #endregion

        #region Commands

        /// <summary>
        /// Gets or sets the save command.
        /// </summary>
        public RelayCommand SaveCommand { get; set; }

        /// <summary>
        /// Gets or sets the add ids command.
        /// </summary>
        public RelayCommand AddIdsCommand { get; set; }

        /// <summary>
        /// Gets or sets the remove ids command.
        /// </summary>
        public RelayCommand RemoveIdsCommand { get; set; }

        /// <summary>
        /// Gets or sets the remove machine command.
        /// </summary>
        public RelayCommand RemoveMachineCommand { get; set; }

        /// <summary>
        /// Gets or sets the add machine command.
        /// </summary>
        public RelayCommand AddMachineCommand { get; set; }

        /// <summary>
        /// Gets or sets the edit machine command.
        /// </summary>
        public RelayCommand EditMachineCommand { get; set; }

        /// <summary>
        /// Gets or sets the back to machines command.
        /// </summary>
        public RelayCommand BackToMachinesCommand { get; set; }

        /// <summary>
        /// Gets or sets the add spool command.
        /// </summary>
        public RelayCommand AddSpoolCommand { get; set; }

        /// <summary>
        /// Gets or sets the remove spool command.
        /// </summary>
        public RelayCommand RemoveSpoolCommand { get; set; }

        /// <summary>
        /// Gets or sets the clone machine command.
        /// </summary>
        public RelayCommand CloneMachineCommand { get; set; }

        /// <summary>
        /// Gets or sets the reset device registration command.
        /// </summary>
        public RelayCommand ResetDeviceRegistrationCommand { get; set; }

        /// <summary>
        /// Gets or sets the make prototype command.
        /// </summary>
        public RelayCommand MakePrototypeCommand { get; set; }

        public RelayCommand UpgradeToGen2Command { get; set; }
        #endregion

        #region Constructors

        public MainViewVM()
        {

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MainViewVM"/> class.
        /// </summary>
        public MainViewVM(INotificationProvider notification, IAuthenticationProvider authentication, IActionLogManager actionLogManager)
        {
            _notification = notification;
            _authentication = authentication;
            _actionLogManager = actionLogManager;

            Machines = new List<MachineModel>();

            _machines_action_timer = new ActionTimer(TimeSpan.FromMilliseconds(200));
            _dispensers_action_timer = new ActionTimer(TimeSpan.FromMilliseconds(200));

            AddIdsCommand = new RelayCommand(AddIds, (x) => ActiveMachine != null && ActiveMachine.Configuration != null && ActiveMachine.Configuration.IdsPacks.Count < 10);
            RemoveIdsCommand = new RelayCommand(RemoveIds, (x) => SelectedIds != null);
            EditMachineCommand = new RelayCommand(() => LoadSelectedMachine(), () => SelectedMachine != null);
            BackToMachinesCommand = new RelayCommand(() => View.NavigateTo(MachineDesignerNavigationView.MachinesView));
            SaveCommand = new RelayCommand(SaveMachine);
            AddMachineCommand = new RelayCommand(AddNewMachine);
            RemoveMachineCommand = new RelayCommand(RemoveSelectedMachine, () => SelectedMachine != null);
            AddSpoolCommand = new RelayCommand(AddNewSpool);
            RemoveSpoolCommand = new RelayCommand(RemoveSpool, () => SelectedSpool != null);
            CloneMachineCommand = new RelayCommand(CloneMachine, () => SelectedMachine != null);
            ResetDeviceRegistrationCommand = new RelayCommand(ResetDeviceRegistration);
            MakePrototypeCommand = new RelayCommand(MakePrototype);

            MachineUpdatesViewVM = new MachineUpdatesViewVM(_notification);
            TupViewVM = new TupViewVM(_notification);

            UpgradeToGen2Command = new RelayCommand(UpgradeToGEN2);
        }

        #endregion

        public override void OnApplicationReady()
        {
            
        }

        private void OnDispensersFilterChanged()
        {
            if (!String.IsNullOrWhiteSpace(DispensersFilter))
            {
                _dispensers_action_timer.ResetReplace(() =>
                {
                    Task.Factory.StartNew(() =>
                    {
                        ActiveMachineAdapter.Dispensers = ActiveMachineAdapter.Context.Dispensers.Where(x => x.SerialNumber.ToLower().StartsWith(DispensersFilter.ToLower())).OrderBy(x => x.SerialNumber).ToSynchronizedObservableCollection();
                    });
                });
            }
        }

        #region Drag Drop Handlers

        /// <summary>
        /// Drops the ids pack.
        /// </summary>
        /// <param name="idsPack1">The ids pack1.</param>
        /// <param name="idsPack2">The ids pack2.</param>
        public void DropIdsPack(IdsPack idsPack1, IdsPack idsPack2)
        {
            ActiveMachine.Configuration.IdsPacks.Swap(idsPack1, idsPack2);
            ColorCalibrationViewVM.InvalidateCalibrationDataAndColorConversion();
        }

        /// <summary>
        /// Drops the touch panel.
        /// </summary>
        /// <param name="applicationDisplayPanelVersion">The application display panel version.</param>
        public void DropTouchPanel(ApplicationDisplayPanelVersion applicationDisplayPanelVersion)
        {
            ActiveMachine.Configuration.ApplicationDisplayPanelVersion = applicationDisplayPanelVersion;
            ActiveMachine.Configuration.ApplicationDisplayPanelVersionGuid = applicationDisplayPanelVersion.Guid;
        }

        /// <summary>
        /// Drops the application firmware version.
        /// </summary>
        /// <param name="applicationFirmwareVersion">The application firmware version.</param>
        public void DropApplicationFirmwareVersion(ApplicationFirmwareVersion applicationFirmwareVersion)
        {
            ActiveMachine.Configuration.ApplicationFirmwareVersion = applicationFirmwareVersion;
            ActiveMachine.Configuration.ApplicationFirmwareVersionGuid = applicationFirmwareVersion.Guid;
        }

        /// <summary>
        /// Drops the hardware version.
        /// </summary>
        /// <param name="hardwareVersion">The hardware version.</param>
        public void DropHardwareVersion(HardwareVersion hardwareVersion)
        {
            ActiveMachine.Configuration.HardwareVersion = hardwareVersion;
            ActiveMachine.Configuration.HardwareVersionGuid = hardwareVersion.Guid;
        }

        /// <summary>
        /// Drops the embedded firmware.
        /// </summary>
        /// <param name="embeddedFirmwareVersion">The embedded firmware version.</param>
        public void DropEmbeddedFirmware(EmbeddedFirmwareVersion embeddedFirmwareVersion)
        {
            ActiveMachine.Configuration.EmbeddedFirmwareVersion = embeddedFirmwareVersion;
            ActiveMachine.Configuration.EmbeddedFirmwareVersionGuid = embeddedFirmwareVersion.Guid;
        }

        /// <summary>
        /// Drops the application os version.
        /// </summary>
        /// <param name="applicationOsVersion">The application os version.</param>
        public void DropApplicationOsVersion(ApplicationOsVersion applicationOsVersion)
        {
            ActiveMachine.Configuration.ApplicationOsVersion = applicationOsVersion;
            ActiveMachine.Configuration.ApplicationOsVersionGuid = applicationOsVersion.Guid;
        }

        /// <summary>
        /// Drops the dispenser.
        /// </summary>
        /// <param name="dispenser">The dispenser.</param>
        /// <param name="idsPack">The ids pack.</param>
        public void DropDispenser(Dispenser dispenser, IdsPack idsPack)
        {
            idsPack.Dispenser = dispenser;
            idsPack.DispenserGuid = dispenser.Guid;
        }

        /// <summary>
        /// Drops the type of the cartridge.
        /// </summary>
        /// <param name="cartridgeType">Type of the cartridge.</param>
        /// <param name="idsPack">The ids pack.</param>
        public void DropCartridgeType(CartridgeType cartridgeType, IdsPack idsPack)
        {
            idsPack.CartridgeType = cartridgeType;
            idsPack.CartridgeTypeGuid = cartridgeType.Guid;
        }

        /// <summary>
        /// Drops the type of the mid tank.
        /// </summary>
        /// <param name="midTankType">Type of the mid tank.</param>
        /// <param name="idsPack">The ids pack.</param>
        public void DropMidTankType(MidTankType midTankType, IdsPack idsPack)
        {
            idsPack.MidTankType = midTankType;
            idsPack.MidTankTypeGuid = midTankType.Guid;
        }

        /// <summary>
        /// Drops the type of the liquid.
        /// </summary>
        /// <param name="liquidType">Type of the liquid.</param>
        /// <param name="idsPack">The ids pack.</param>
        public void DropLiquidType(LiquidType liquidType, IdsPack idsPack)
        {
            idsPack.LiquidType = liquidType;
            idsPack.LiquidTypeGuid = liquidType.Guid;
            ColorCalibrationViewVM.InvalidateCalibrationDataAndColorConversion();
        }

        /// <summary>
        /// Drops the ids formula.
        /// </summary>
        /// <param name="idsPackFormula">The ids pack formula.</param>
        /// <param name="idsPack">The ids pack.</param>
        /// <exception cref="NotImplementedException"></exception>
        public void DropIdsFormula(IdsPackFormula idsPackFormula, IdsPack idsPack)
        {
            idsPack.IdsPackFormula = idsPackFormula;
            idsPack.IdsPackFormulaGuid = idsPackFormula.Guid;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Removes the selected IDS pack.
        /// </summary>
        private void RemoveIds()
        {
            ActiveMachineAdapter.Context.IdsPacks.Remove(SelectedIds);
            SelectedIds = null;
            ColorCalibrationViewVM.InvalidateCalibrationDataAndColorConversion();
        }

        /// <summary>
        /// Adds a new IDS pack.
        /// </summary>
        private void AddIds()
        {
            ActiveMachineAdapter.Context.IdsPacks.Add(new IdsPack() { Configuration = ActiveMachine.Configuration });
            InvalidateRelayCommands();
        }

        private async void LoadSelectedMachine(bool newMachine = false, bool clone = false, MachineCreationDialogVM machineCreationDialogVM = null)
        {
            IsNewMachine = false;

            using (_notification.PushTaskItem("Loading machine details..."))
            {
                try
                {
                    IsFree = false;

                    if (ActiveMachineAdapter != null)
                    {
                        ActiveMachineAdapter.Context.Dispose();
                    }

                    ActiveMachineAdapter = new ObservablesStaticCollections(ObservablesContext.CreateDefault());
                    ActiveMachineAdapter.Organizations = (await ActiveMachineAdapter.Context.Organizations.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.MachineVersions = (await ActiveMachineAdapter.Context.MachineVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.Rmls = (await ActiveMachineAdapter.Context.Rmls.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.ColorSpaces = (await ActiveMachineAdapter.Context.ColorSpaces.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.SpoolTypes = (await ActiveMachineAdapter.Context.SpoolTypes.ToListAsync()).ToObservableCollection();

                    ActiveMachineAdapter.ApplicationDisplayPanelVersions = (await ActiveMachineAdapter.Context.ApplicationDisplayPanelVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.ApplicationFirmwareVersions = (await ActiveMachineAdapter.Context.ApplicationFirmwareVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.ApplicationOsVersions = (await ActiveMachineAdapter.Context.ApplicationOsVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.EmbeddedFirmwareVersions = (await ActiveMachineAdapter.Context.EmbeddedFirmwareVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.DispenserTypes = (await ActiveMachineAdapter.Context.DispenserTypes.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.LiquidTypes = (await ActiveMachineAdapter.Context.LiquidTypes.ToListAsync()).OrderBy(x => x.PreferredIndex).ToObservableCollection();
                    ActiveMachineAdapter.MidTankTypes = (await ActiveMachineAdapter.Context.MidTankTypes.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.CartridgeTypes = (await ActiveMachineAdapter.Context.CartridgeTypes.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.IdsPackFormulas = (await ActiveMachineAdapter.Context.IdsPackFormulas.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.HardwareVersions = (await ActiveMachineAdapter.Context.HardwareVersions.ToListAsync()).OrderByDescending(x => x.Version).ToObservableCollection();
                    ActiveMachineAdapter.MachineVersions = (await ActiveMachineAdapter.Context.MachineVersions.ToListAsync()).ToObservableCollection();
                    ActiveMachineAdapter.Organizations = (await ActiveMachineAdapter.Context.Organizations.ToListAsync()).ToObservableCollection();

                    bool initHwConfig = true;

                    Configuration machineConfigBeforeClone = null;

                    if (!newMachine)
                    {
                        ActiveMachine = (await new MachineBuilder(ActiveMachineAdapter.Context).Set(SelectedMachine.Guid).WithOrganization().WithConfiguration().WithSpools().BuildAsync());
                        _machineBeforeSave = MachineDTO.FromObservable(ActiveMachine);


                        if (clone)
                        {
                            machineConfigBeforeClone = ActiveMachine.Configuration;

                            IsNewMachine = true;
                            ActiveMachine = ActiveMachine.Clone();
                            ActiveMachine.Name = machineCreationDialogVM.Name;
                            ActiveMachine.SerialNumber = machineCreationDialogVM.SerialNumber;
                            ActiveMachine.IsDeviceRegistered = false;
                            ActiveMachine.DeviceId = null;
                            ActiveMachine.DeviceName = null;
                            ActiveMachine.ActivationKey = null;
                            ActiveMachineAdapter.Context.Machines.Add(ActiveMachine);
                        }
                    }
                    else
                    {
                        IsNewMachine = true;

                        if (machineCreationDialogVM.SelectedPrototype == null)
                        {
                            ActiveMachine = new Machine();
                            ActiveMachine.Configuration = new Configuration();
                            ActiveMachine.SerialNumber = machineCreationDialogVM.SerialNumber;
                            ActiveMachine.Name = machineCreationDialogVM.Name;
                            ActiveMachineAdapter.Context.Machines.Add(ActiveMachine);
                        }
                        else
                        {
                            try
                            {
                                initHwConfig = false;
                                ActiveMachine = machineCreationDialogVM.SelectedPrototype.CreateMachine(machineCreationDialogVM.SerialNumber, machineCreationDialogVM.Name);
                                ActiveMachine.MachineVersion = ActiveMachineAdapter.MachineVersions.FirstOrDefault(x => x.Guid == ActiveMachine.MachineVersionGuid);

                                if (machineCreationDialogVM.SelectedHardwareVersion != null)
                                {
                                    ActiveMachine.Configuration.HardwareVersion = ActiveMachineAdapter.HardwareVersions.FirstOrDefault(x => x.Guid == machineCreationDialogVM.SelectedHardwareVersion.Guid);
                                }

                                ActiveMachineAdapter.Context.Machines.Add(ActiveMachine);

                                HardwareConfigurationViewVM = new HardwareConfigurationViewVM(_notification);
                                var version = await new HardwareVersionBuilder(ActiveMachineAdapter.Context).Set(ActiveMachine.Configuration.HardwareVersionGuid).WithHardwareComponents().BuildAsync();
                                HardwareConfigurationViewVM.Init(ActiveMachine.Configuration);
                            }
                            catch (Exception ex)
                            {
                                _notification.ShowError($"Invalid machine prototype.\n{ex.FlattenMessage()}");
                                View.NavigateTo(MachineDesignerNavigationView.MachinesView);
                                return;
                            }
                        }
                    }

                    ActiveMachine.OrganizationChanged -= ActiveMachine_OrganizationChanged;
                    ActiveMachine.OrganizationChanged += ActiveMachine_OrganizationChanged;

                    _all_sites = await ActiveMachineAdapter.Context.Sites.ToListAsync();

                    var sites = ActiveMachine.Organization != null ? _all_sites.Where(x => x.OrganizationGuid == ActiveMachine.OrganizationGuid).ToList() : new List<Site>();
                    sites.Insert(0, new Site() { Name = "NONE", ID = -1 });
                    Sites = sites;

                    SelectedSite = Sites.SingleOrDefault(x => x.Guid == ActiveMachine.SiteGuid);

                    ColorCalibrationViewVM = new ColorCalibrationViewVM(_notification, ActiveMachine, _activeMachineAdapter.Context, ActiveMachineAdapter.Rmls.FirstOrDefault())
                    {
                        Rmls = ActiveMachineAdapter.Rmls,
                        LiquidTypesRmls = ActiveMachineAdapter.Rmls.FirstOrDefault().LiquidTypesRmls,
                    };

                    await ColorCalibrationViewVM.Invalidate();

                    if (initHwConfig)
                    {
                        HardwareConfigurationViewVM = new HardwareConfigurationViewVM(_notification);
                        HardwareConfigurationViewVM.Init(ActiveMachine.Configuration);
                    }

                    if (!IsNewMachine)
                    {
                        await MachineUpdatesViewVM.Init(ActiveMachine, ActiveMachineAdapter.Context);
                        TupViewVM.Init(ActiveMachine);
                    }

                    ActiveMachine.Configuration.HardwareVersionChanged += Configuration_HardwareVersionChanged;

                    while (ActiveMachine.ActivationKey == null) //Generate a random password and make sure no machine matches it.
                    {
                        ActiveMachine.ActivationKey = PasswordGenerator.Generate(8, PasswordGenerator.PasswordType.Alpha, PasswordGenerator.PasswordCasing.Upper);
                        if (await ActiveMachineAdapter.Context.Machines.Where(x => x.ActivationKey == ActiveMachine.ActivationKey).CountAsync() == 0)
                        {
                            break;
                        }

                        ActiveMachine.ActivationKey = null;
                    }

                    IsGen1Machine = !ActiveMachine.Configuration.NoneEmptyIdsPacks.Any(x => x.LiquidType.Type == LiquidTypes.LightCyan);

                    View.NavigateTo(MachineDesignerNavigationView.MachineDetailsView);
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, $"Error loading machine details for serial number {SelectedMachine.SerialNumber}");
                    _notification.ShowError($"An error occurred while trying to load the selected machine details.\n{ex.Message}");
                }
                finally
                {
                    IsFree = true;
                    InvalidateRelayCommands();
                }
            }
        }

        private async void Configuration_HardwareVersionChanged(object sender, HardwareVersion e)
        {
            var version = ActiveMachine.Configuration.HardwareVersion;

            if (version != null)
            {
                using (_notification.PushTaskItem("Loading hardware version..."))
                {
                    try
                    {
                        if (HardwareConfigurationViewVM == null)
                        {
                            HardwareConfigurationViewVM = new HardwareConfigurationViewVM(_notification);
                        }

                        version = await new HardwareVersionBuilder(ActiveMachineAdapter.Context).Set(version.Guid).WithHardwareComponents().BuildAsync();
                        HardwareConfigurationViewVM.Init(ActiveMachine.Configuration);
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex);
                    }
                }
            }
        }

        private async void SaveMachine()
        {
            foreach (var ids in ActiveMachine.Configuration.IdsPacks)
            {
                ids.PackIndex = ActiveMachine.Configuration.IdsPacks.IndexOf(ids);
                ids.Configuration = ActiveMachine.Configuration;
                ids.ConfigurationGuid = ActiveMachine.Configuration.Guid;
            }

            List<String> errors = new List<string>();

            if (ActiveMachine.MachineVersion == null)
            {
                errors.Add("Machine version is required.");
            }

            if (ActiveMachine.Name.IsNullOrWhiteSpace())
            {
                errors.Add("Machine name is required.");
            }

            if (ActiveMachine.Organization == null)
            {
                errors.Add("Machine organization is required.");
            }

            if (ActiveMachine.SerialNumber.IsNullOrWhiteSpace())
            {
                errors.Add("Machine serial number is required.");
            }

            if (ActiveMachine.Configuration.ApplicationDisplayPanelVersion == null)
            {
                errors.Add("Touch Panel is required.");
            }

            if (ActiveMachine.Configuration.ApplicationFirmwareVersion == null)
            {
                errors.Add("Application firmware is required.");
            }

            if (ActiveMachine.Configuration.ApplicationOsVersion == null)
            {
                errors.Add("Application operation system is required.");
            }

            if (ActiveMachine.Configuration.EmbeddedFirmwareVersion == null)
            {
                errors.Add("Embedded firmware is required.");
            }

            if (ActiveMachine.Configuration.HardwareVersion == null)
            {
                errors.Add("Hardware version is required.");
            }

            foreach (var pack in ActiveMachine.Configuration.IdsPacks)
            {
                if (pack.LiquidType != null || pack.CartridgeType != null || pack.Dispenser != null || pack.IdsPackFormula != null || pack.MidTankType != null)
                {
                    if (pack.CartridgeType == null)
                    {
                        errors.Add(String.Format("Cartridge type is required on IDS pack '{0}'.", pack.PackIndex));
                    }
                    if (pack.Dispenser == null)
                    {
                        errors.Add(String.Format("Dispenser is required on IDS pack '{0}'.", pack.PackIndex));
                    }
                    if (pack.LiquidType == null)
                    {
                        errors.Add(String.Format("Liquid type is required on IDS pack '{0}'.", pack.PackIndex));
                    }
                    if (pack.MidTankType == null)
                    {
                        errors.Add(String.Format("Mid Tank type is required on IDS pack '{0}'.", pack.PackIndex));
                    }
                    if (pack.IdsPackFormula == null)
                    {
                        errors.Add(String.Format("Formula type is required on IDS pack '{0}'.", pack.PackIndex));
                    }

                    pack.IsEmpty = false;
                }
                else
                {
                    pack.IsEmpty = true;
                }
            }

            foreach (var pack in ActiveMachine.Configuration.IdsPacks.Where(x => x.Dispenser != null))
            {
                if (ActiveMachine.Configuration.IdsPacks.Where(x => x.Dispenser == pack.Dispenser).Count() > 1)
                {
                    errors.Add($"Dispenser '{pack.Dispenser.SerialNumber}' is installed on multiple IDS packs.");
                }

                if (ActiveMachineAdapter.Context.IdsPacks.Count(x => x.ConfigurationGuid != pack.ConfigurationGuid && x.DispenserGuid == pack.DispenserGuid) > 0)
                {
                    errors.Add($"Dispenser '{pack.Dispenser.SerialNumber}' is already installed on a different machine.");
                }
            }

            if (ActiveMachine.Spools.GroupBy(x => x.SpoolType).Any(x => x.Count() > 1))
            {
                errors.Add($"Same spool type is registered multiple times.");
            }

            if (errors.Count > 0)
            {
                String errorsString = "Please fix the following validation errors before trying to save." + Environment.NewLine + Environment.NewLine;
                errorsString += String.Join(Environment.NewLine, errors.Distinct());
                _notification.ShowError(errorsString);
                return;
            }

            try
            {
                IsFree = false;

                using (_notification.PushTaskItem("Saving Machine Details..."))
                {

                    ActiveMachine.ConfigurationGuid = ActiveMachine.Configuration.Guid;
                    ActiveMachine.LastUpdated = DateTime.UtcNow;
                    ActiveMachine.SiteGuid = SelectedSite == null ? null : (SelectedSite.ID == -1 ? null : SelectedSite.Guid);

                    ColorCalibrationViewVM.Save();

                    var hwConfig = HardwareConfigurationViewVM.GetResultingHardwareConfiguration();
                    ActiveMachine.Configuration.SetHardwareConfiguration(hwConfig);
                    await ActiveMachineAdapter.Context.SaveChangesAsync();

                    if (IsNewMachine)
                    {
                        _actionLogManager.InsertLog(BL.Enumerations.ActionLogType.MachineCreated, _authentication.CurrentUser, ActiveMachine.Name, ActiveMachine, "New machine created using Machine Studio.");
                    }
                    else
                    {
                        var machineAfterDTO = MachineDTO.FromObservable(ActiveMachine);
                        _actionLogManager.InsertLog(BL.Enumerations.ActionLogType.MachineSaved, _authentication.CurrentUser, _machineBeforeSave.Name, _machineBeforeSave, machineAfterDTO, "Machine saved using Machine Studio.");
                        _machineBeforeSave = machineAfterDTO;
                    }

                    if (SelectedMachine != null)
                    {
                        await LoadMachines();
                    }

                    await ColorCalibrationViewVM.Invalidate();
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error saving machine details.");
                _notification.ShowError("An error occurred while trying to save the machine details" + Environment.NewLine + ex.Message);
            }
            finally
            {
                IsFree = true;
                InvalidateRelayCommands();
            }
        }

        private async void AddNewMachine()
        {
            List<MachinePrototype> prototypes = new List<MachinePrototype>();
            List<HardwareVersion> hardwareVersions = new List<HardwareVersion>();

            try
            {
                IsFree = false;

                using (_notification.PushTaskItem("Loading machine creation data..."))
                {
                    using (ObservablesContext db = ObservablesContext.CreateDefault())
                    {
                        prototypes = await db.MachinePrototypes.ToListAsync();
                        hardwareVersions = await db.HardwareVersions.ToListAsync();
                    }
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error loading machine creation data.");
                _notification.ShowError($"Error loading machine creation data\n{ex.FlattenMessage()}");
                return;
            }
            finally
            {
                IsFree = true;
            }

            MachineCreationDialogVM vm = new MachineCreationDialogVM();
            vm.IsNewMachine = true;
            vm.Prototypes = prototypes.ToList();
            vm.HardwareVersions = hardwareVersions.OrderByDescending(x => x.Version).ToList();
            vm.SelectedHardwareVersion = vm.HardwareVersions.FirstOrDefault();
            _notification.ShowModalDialog<MachineCreationDialogVM, Views.MachineCreationDialog>(vm, (x) =>
            {
                using (ObservablesContext db = ObservablesContext.CreateDefault())
                {
                    if (db.Machines.Any(y => y.SerialNumber == vm.SerialNumber || y.Name.ToLower() == vm.Name.ToLower()))
                    {
                        _notification.ShowError("Machine serial number or name already exists.");
                        return;
                    }
                }

                LoadSelectedMachine(true, false, vm);
            }, () => { });
        }

        private async void RemoveSelectedMachine()
        {
            if (SelectedMachine == null) return;

            if (_notification.ShowQuestion("Are you sure you want to delete the selected machine?"))
            {
                using (_notification.PushTaskItem("Removing machine..."))
                {
                    try
                    {
                        IsFree = false;

                        var selectedMachine = SelectedMachine;

                        using (ObservablesContext db = ObservablesContext.CreateDefault())
                        {
                            var machineToDelete = await new MachineBuilder(db).Set(SelectedMachine.Guid).WithConfiguration().BuildAsync();

                            await machineToDelete.DeleteCascadeAsync(db);
                            await machineToDelete.Configuration.DeleteCascadeAsync(db);
                            foreach (var dispenser in machineToDelete.Configuration.NoneEmptyIdsPacks.Select(x => x.Dispenser))
                            {
                                if (dispenser != null)
                                {
                                    await dispenser.DeleteCascadeAsync(db);
                                }
                            }

                            _actionLogManager.InsertLog(ActionLogType.MachineDeleted, _authentication.CurrentUser, selectedMachine.Name, machineToDelete, "Machine deleted using Machine Studio.");

                            await LoadMachines();
                        }
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, $"Error removing selected machine.");
                        _notification.ShowError($"An error occurred while trying to delete the selected machine.\n{ex.Message}");
                    }
                    finally
                    {
                        IsFree = true;
                    }
                }
            }
        }

        private void ResetDeviceRegistration()
        {
            if (_notification.ShowQuestion("Are you sure you wish to reset this machine device registration?"))
            {
                ActiveMachine.IsDeviceRegistered = false;
                ActiveMachine.DeviceId = null;
                ActiveMachine.DeviceName = null;
            }
        }

        private async void MakePrototype()
        {
            String protoName = _notification.ShowTextInput("Enter prototype name", "name");

            if (!protoName.IsNotNullOrWhiteSpace()) return;

            using (_notification.PushTaskItem($"Saving machine prototype '{protoName}'..."))
            {
                try
                {
                    IsFree = false;

                    using (var db = ObservablesContext.CreateDefault())
                    {
                        MachinePrototype prototype = MachinePrototype.CreateNew(ActiveMachine, protoName, protoName);
                        db.MachinePrototypes.Add(prototype);
                        await db.SaveChangesAsync();
                    }
                }
                catch (Exception ex)
                {
                    _notification.ShowError($"Error creating machine prototype\n{ex.FlattenMessage()}");
                }
                finally
                {
                    IsFree = true;
                }
            }
        }

        #endregion

        private void CloneMachine()
        {
            MachineCreationDialogVM vm = new MachineCreationDialogVM();
            vm.IsNewMachine = false;
            _notification.ShowModalDialog<MachineCreationDialogVM, Views.MachineCreationDialog>(vm, (x) =>
            {
                using (ObservablesContext db = ObservablesContext.CreateDefault())
                {
                    if (db.Machines.Any(y => y.SerialNumber == vm.SerialNumber || y.Name.ToLower() == vm.Name.ToLower()))
                    {
                        _notification.ShowError("Machine serial number or name already exists.");
                        return;
                    }
                }

                LoadSelectedMachine(false, true, vm);
            }, () => { });
        }

        private void AddNewSpool()
        {
            _activeMachineAdapter.Context.Spools.Add(new Spool()
            {
                Machine = ActiveMachine,
            });
        }

        private void RemoveSpool()
        {
            if (SelectedSpool != null)
            {
                _activeMachineAdapter.Context.Spools.Remove(SelectedSpool);
                ActiveMachine.Spools.Remove(SelectedSpool);
            }
        }

        private async void OnFilterChanged()
        {
            await LoadMachines();
        }

        private async Task LoadMachines()
        {
            String filter = Filter.ToStringOrEmpty();

            using (ObservablesContext db = ObservablesContext.CreateDefault())
            {
                var machines = await db.Machines.Where(x => filter == String.Empty || x.SerialNumber.ToLower().StartsWith(filter.ToLower()) || x.Name.ToLower().Contains(filter.ToLower()))
                    .Include(x => x.Configuration)
                    .Include(x => x.MachineVersion.Name)
                    .Include(x => x.Organization.Name)
                    .Include(x => x.Configuration.HardwareVersion.Name)
                    .Include(x => x.Configuration.HardwareVersion.Version).Select(x => new
                    {
                        x.Guid,
                        x.SerialNumber,
                        x.Name,
                        Organization = x.Organization.Name,
                        MachineVersion = x.MachineVersion.Name,
                        HardwareVersionName = x.Configuration.HardwareVersion.Name,
                        HardwareVersionVersion = x.Configuration.HardwareVersion.Version,
                        x.IsDeviceRegistered,
                        x.IsDemo,
                        x.LastUpdated
                    }).OrderBy(x => x.SerialNumber).ToListAsync();

                List<MachineModel> models = new List<MachineModel>();

                foreach (var machine in machines)
                {
                    MachineModel model = new MachineModel();
                    model.Guid = machine.Guid;
                    model.SerialNumber = machine.SerialNumber;
                    model.Name = machine.Name;
                    model.Organization = machine.Organization;
                    model.HardwareVersion = machine.HardwareVersionName + " v" + machine.HardwareVersionVersion;
                    model.MachineVersion = machine.MachineVersion;
                    model.IsDeviceRegistered = machine.IsDeviceRegistered;
                    model.IsDemo = machine.IsDemo;
                    model.LastUpdated = machine.LastUpdated;
                    models.Add(model);
                }

                Machines = models;
            }
        }

        private void ActiveMachine_OrganizationChanged(object sender, Organization e)
        {
            var sites = ActiveMachine.Organization != null ? _all_sites.Where(x => x.OrganizationGuid == ActiveMachine.OrganizationGuid).ToList() : new List<Site>();
            sites.Insert(0, new Site() { Name = "NONE", ID = -1 });
            Sites = sites;

            SelectedSite = Sites.SingleOrDefault(x => x.Guid == ActiveMachine.SiteGuid);
        }

        private async void UpgradeToGEN2()
        {
            if (!_notification.ShowQuestion("Are you sure you want to upgrade this machine configuration to GEN 2 standards?")) return;

            using (_notification.PushTaskItem("Upgrading this machine to GEN 2..."))
            {
                try
                {
                    IsFree = false;

                    var configuration = ActiveMachine.Configuration;

                    InvalidOperationException noneGen1Exception = new InvalidOperationException("This machine configuration does not fit within the GEN 1 standard and cannot be upgraded.");

                    var orderedPacks = configuration.IdsPacks.OrderBy(x => x.PackIndex).ToList();

                    try
                    {
                        if (!ValidateIdsPack(orderedPacks[0], LiquidTypes.Black, MidTankTypes.StandardMidTank, DispenserTypes.StandardDispenser, IdsPackFormulas.StandardColor)) throw noneGen1Exception;
                        if (!ValidateIdsPack(orderedPacks[1], LiquidTypes.Cyan, MidTankTypes.StandardMidTank, DispenserTypes.StandardDispenser, IdsPackFormulas.StandardColor)) throw noneGen1Exception;
                        if (!ValidateIdsPack(orderedPacks[2], LiquidTypes.Magenta, MidTankTypes.StandardMidTank, DispenserTypes.StandardDispenser, IdsPackFormulas.StandardColor)) throw noneGen1Exception;
                        if (!ValidateIdsPack(orderedPacks[3], LiquidTypes.Yellow, MidTankTypes.StandardMidTank, DispenserTypes.StandardDispenser, IdsPackFormulas.StandardColor)) throw noneGen1Exception;
                        if (!ValidateIdsPack(orderedPacks[4], LiquidTypes.TransparentInk, MidTankTypes.StandardMidTank, DispenserTypes.StandardDispenser, IdsPackFormulas.TransparentLiquid)) throw noneGen1Exception;
                        if (!orderedPacks[5].IsEmpty) throw noneGen1Exception;
                        if (!ValidateIdsPack(orderedPacks[6], LiquidTypes.Cleaner, MidTankTypes.StandardMidTank, DispenserTypes.StandardDispenser, IdsPackFormulas.CleanerLiquid)) throw noneGen1Exception;
                        if (!ValidateIdsPack(orderedPacks[7], LiquidTypes.Lubricant, MidTankTypes.StandardMidTank, DispenserTypes.StandardDispenser, IdsPackFormulas.Lubricant)) throw noneGen1Exception;
                    }
                    catch
                    {
                        throw noneGen1Exception;
                    }

                    var cleanerPack = orderedPacks[6];
                    var lubricantPack = orderedPacks[7];
                    var emptyPack = orderedPacks[5];

                    cleanerPack.MidTankType = ActiveMachineAdapter.MidTankTypes.First(x => x.Type == MidTankTypes.NoMidTank);
                    cleanerPack.IdsPackFormula = ActiveMachineAdapter.IdsPackFormulas.First(x => x.Type == IdsPackFormulas.LightInksCleanerLiquid);
                    cleanerPack.Dispenser.DispenserType = ActiveMachineAdapter.DispenserTypes.First(x => x.Type == DispenserTypes.CleanerDispenser);
                    cleanerPack.PackIndex = 8;

                    lubricantPack.MidTankType = ActiveMachineAdapter.MidTankTypes.First(x => x.Type == MidTankTypes.LubricantMidTank);
                    lubricantPack.IdsPackFormula = ActiveMachineAdapter.IdsPackFormulas.First(x => x.Type == IdsPackFormulas.LightInksLubricantLiquid);
                    lubricantPack.Dispenser.DispenserType = ActiveMachineAdapter.DispenserTypes.First(x => x.Type == DispenserTypes.LubricantDispenser);
                    lubricantPack.PackIndex = 9;

                    var lightCyanDispenser = await ActiveMachineAdapter.Context.Dispensers.Include(x => x.IdsPacks).FirstOrDefaultAsync(x => x.SerialNumber.StartsWith(ActiveMachine.SerialNumber) && x.IdsPacks.Count == 0);

                    int newDispenserStartIndex = 9;

                    if (lightCyanDispenser == null)
                    {
                        lightCyanDispenser = new Dispenser();
                        lightCyanDispenser.NlPerPulse = orderedPacks[1].Dispenser.NlPerPulse;
                        lightCyanDispenser.SerialNumber = $"{ActiveMachine.SerialNumber}-{newDispenserStartIndex++}";
                        lightCyanDispenser.DispenserType = ActiveMachineAdapter.DispenserTypes.First(x => x.Type == DispenserTypes.StandardDispenser);
                    }

                    IdsPack lightCyanPack = new IdsPack();
                    lightCyanPack.PackIndex = 5;
                    lightCyanPack.LiquidType = ActiveMachineAdapter.LiquidTypes.First(x => x.Type == LiquidTypes.LightCyan);
                    lightCyanPack.MidTankType = ActiveMachineAdapter.MidTankTypes.First(x => x.Type == MidTankTypes.StandardMidTank);
                    lightCyanPack.IdsPackFormula = ActiveMachineAdapter.IdsPackFormulas.First(x => x.Type == IdsPackFormulas.LightColor);
                    lightCyanPack.CartridgeType = ActiveMachineAdapter.CartridgeTypes.First(x => x.Type == CartridgeTypes.StandardCartridge);
                    lightCyanPack.Dispenser = lightCyanDispenser;
                    configuration.IdsPacks.Add(lightCyanPack);

                    IdsPack lightMagentaPack = new IdsPack();
                    lightMagentaPack.PackIndex = 6;
                    lightMagentaPack.LiquidType = ActiveMachineAdapter.LiquidTypes.First(x => x.Type == LiquidTypes.LightMagenta);
                    lightMagentaPack.MidTankType = ActiveMachineAdapter.MidTankTypes.First(x => x.Type == MidTankTypes.StandardMidTank);
                    lightMagentaPack.IdsPackFormula = ActiveMachineAdapter.IdsPackFormulas.First(x => x.Type == IdsPackFormulas.LightColor);
                    lightMagentaPack.CartridgeType = ActiveMachineAdapter.CartridgeTypes.First(x => x.Type == CartridgeTypes.StandardCartridge);
                    lightMagentaPack.Dispenser = new Dispenser()
                    {
                        NlPerPulse = orderedPacks[2].Dispenser.NlPerPulse,
                        SerialNumber = $"{ActiveMachine.SerialNumber}-{newDispenserStartIndex++}",
                        DispenserType = ActiveMachineAdapter.DispenserTypes.First(x => x.Type == DispenserTypes.StandardDispenser)
                    };
                    configuration.IdsPacks.Add(lightMagentaPack);

                    IdsPack lightYellowPack = new IdsPack();
                    lightYellowPack.PackIndex = 7;
                    lightYellowPack.LiquidType = ActiveMachineAdapter.LiquidTypes.First(x => x.Type == LiquidTypes.LightYellow);
                    lightYellowPack.MidTankType = ActiveMachineAdapter.MidTankTypes.First(x => x.Type == MidTankTypes.StandardMidTank);
                    lightYellowPack.IdsPackFormula = ActiveMachineAdapter.IdsPackFormulas.First(x => x.Type == IdsPackFormulas.LightColor);
                    lightYellowPack.CartridgeType = ActiveMachineAdapter.CartridgeTypes.First(x => x.Type == CartridgeTypes.StandardCartridge);
                    lightYellowPack.Dispenser = new Dispenser()
                    {
                        NlPerPulse = orderedPacks[3].Dispenser.NlPerPulse,
                        SerialNumber = $"{ActiveMachine.SerialNumber}-{newDispenserStartIndex++}",
                        DispenserType = ActiveMachineAdapter.DispenserTypes.First(x => x.Type == DispenserTypes.StandardDispenser)
                    };
                    configuration.IdsPacks.Add(lightYellowPack);

                    configuration.IdsPacks.Remove(emptyPack);
                    ActiveMachineAdapter.Context.IdsPacks.Remove(emptyPack);

                    var packs = configuration.IdsPacks.ToList();
                    configuration.IdsPacks.Clear();

                    foreach (var pack in packs.OrderBy(x => x.PackIndex))
                    {
                        configuration.IdsPacks.Add(pack);
                    }

                    ActiveMachine.LightInksInstalled = true;
                    ActiveMachine.BtsrInstalled = true;

                    IsGen1Machine = false;

                    _notification.ShowInfo("Machine configuration successfully upgraded to GEN 2.");
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error upgrading machine to GEN 2.");
                    _notification.ShowError($"Error upgrading this machine to GEN 2.\n{ex.FlattenMessage()}");
                }
                finally
                {
                    IsFree = true;
                }
            }
        }

        private bool ValidateIdsPack(IdsPack pack, LiquidTypes liquidType, MidTankTypes midTankType, DispenserTypes dispenserType, IdsPackFormulas formula)
        {
            if (pack.LiquidType.Type != liquidType) return false;
            if (pack.MidTankType.Type != midTankType) return false;
            if (pack.Dispenser.DispenserType.Type != dispenserType) return false;
            if (pack.IdsPackFormula.Type != formula) return false;

            return true;
        }
    }
}