aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.RML/ViewModels/MainViewVM.cs
blob: cadd1fb953f5486e8cc40c2513de1e0cb7195274 (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
241pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Tango.Core;
using Tango.Core.IO;
using Tango.Scripting.Core;
using System.IO;
using Tango.Core.Helpers;

namespace Tango.Scripting.Basic
{
    public class Project<T> : ExtendedObject where T : IContext
    {
        private object _compileLock = new object();

        public String ID { get; set; }

        private String _name;
        public String Name
        {
            get { return _name; }
            set { _name = value; RaisePropertyChangedAuto(); }
        }

        private String _description;
        public String Description
        {
            get { return _description; }
            set { _description = value; RaisePropertyChangedAuto(); }
        }

        private bool _isRunning;
        [JsonIgnore]
        public bool IsRunning
        {
            get { return _isRunning; }
            set { _isRunning = value; RaisePropertyChangedAuto(); }
        }

        private bool _isCompiling;
        [JsonIgnore]
        public bool IsCompiling
        {
            get { return _isCompiling; }
            set { _isCompiling = value; RaisePropertyChangedAuto(); }
        }

        public ApartmentState ApartmentState { get; set; }

        public ObservableCollection<ReferenceAssembly> ReferenceAssemblies { get; set; }

        public ObservableCollection<Script> Scripts { get; set; }

        [JsonIgnore]
        public ObservableCollection<IScriptSource> AdditionalScripts
        {
            get
            {
                return Scripts.Where(x => !x.IsEntryPoint).Cast<IScriptSource>().ToObservableCollection();
            }
        }

        [JsonIgnore]
        public List<ScriptBreakPoint> BreakPoints { get; set; }

        public Project()
        {
            ID = Guid.NewGuid().ToString();

            ApartmentState = ApartmentState.MTA;

            ReferenceAssemblies = new ObservableCollection<ReferenceAssembly>();

            Scripts = new ObservableCollection<Script>();
            Scripts.CollectionChanged += (x, e) => { RaisePropertyChanged(nameof(AdditionalScripts)); };

            BreakPoints = new List<ScriptBreakPoint>();
        }

        public Task<CompilationResult> Compile()
        {
            return Task.Factory.StartNew<CompilationResult>(() =>
            {
                lock (_compileLock)
                {
                    try
                    {
                        IsCompiling = true;
                        var result = new CompilationResult();
                        var tempFolder = TemporaryManager.CreateFolder(Name + "_" + ID);
                        result.TemporaryProjectPath = tempFolder;

                        String mainScriptCode = String.Empty;

                        foreach (var script in Scripts)
                        {
                            script.LoadCount = 0;
                            script.LoadCharCount = 0;
                            String code = script.Code;
                            String codeFile = Path.Combine(tempFolder, script.Name);

                            String loadingString = String.Empty;

                            foreach (var file in Scripts.Where(x => !x.IsEntryPoint && script != x).Select(x => Path.Combine(tempFolder, x.Name)))
                            {
                                loadingString += $"#load \"{file}\"\n";
                                script.LoadCount++;
                            }

                            script.LoadCharCount += loadingString.Length;

                            code = loadingString + code;

                            int debugLinesLength = 0;

                            foreach (var breakPoint in BreakPoints.Where(x => x.Script == script).OrderBy(x => x.LineNumber))
                            {
                                var debugLine = $"context.BreakPoint(\"{script.Name}\",{breakPoint.LineNumber}";

         
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using Tango.BL;
using Tango.BL.Builders;
using Tango.BL.Calibration;
using Tango.BL.Entities;
using Tango.Core.Commands;
using Tango.MachineStudio.Common;
using Tango.MachineStudio.Common.Notifications;
using Tango.MachineStudio.RML.Contracts;
using Tango.MachineStudio.RML.Models;
using Tango.MachineStudio.RML.Views;
using Tango.PMR.ColorLab;
using System.Data.Entity;
using Tango.Core.ExtensionMethods;
using Tango.MachineStudio.Common.Authentication;
using Tango.BL.ActionLogs;
using Tango.BL.DTO;
using Tango.BL.Enumerations;

namespace Tango.MachineStudio.RML.ViewModels
{
    public class MainViewVM : StudioViewModel<IMainView>
    {
        private INotificationProvider _notification;
        private IAuthenticationProvider _authentication;
        private IActionLogManager _actionLogManager;
        private RmlDTO _rmlBeforeSave;

        private ObservablesContext _rmls_context;
        private ObservablesContext _active_context;

        private ObservableCollection<Rml> _rmls;
        public ObservableCollection<Rml> Rmls
        {
            get { return _rmls; }
            set { _rmls = value; RaisePropertyChangedAuto(); }
        }

        private ICollectionView _rmlssCollectionView;
        /// <summary>
        /// Gets or sets the RML collection view.
        /// </summary>
        public ICollectionView RmlsCollectionView
        {
            get { return _rmlssCollectionView; }
            set
            {
                _rmlssCollectionView = value;
                RaisePropertyChangedAuto();
            }
        }

        private ObservableCollection<MediaMaterial> _materials;
        public ObservableCollection<MediaMaterial> Materials
        {
            get { return _materials; }
            set { _materials = value; RaisePropertyChangedAuto(); }
        }

        private ObservableCollection<MediaPurpos> _purposes;
        public ObservableCollection<MediaPurpos> Purposes
        {
            get { return _purposes; }
            set { _purposes = value; RaisePropertyChangedAuto(); }
        }

        private ObservableCollection<MediaCondition> _conditions;
        public ObservableCollection<MediaCondition> Conditions
        {
            get { return _conditions; }
            set { _conditions = value; RaisePropertyChangedAuto(); }
        }

        private ObservableCollection<LinearMassDensityUnit> _linearMassDensityUnits;
        public ObservableCollection<LinearMassDensityUnit> LinearMassDensityUnits
        {
            get { return _linearMassDensityUnits; }
            set { _linearMassDensityUnits = value; RaisePropertyChangedAuto(); }
        }

        private ObservableCollection<FiberShape> _fiberShapes;
        public ObservableCollection<FiberShape> FiberShapes
        {
            get { return _fiberShapes; }
            set { _fiberShapes = value; RaisePropertyChangedAuto(); }
        }

        private ObservableCollection<FiberSynth> _fiberSynths;
        public ObservableCollection<FiberSynth> FiberSynths
        {
            get { return _fiberSynths; }
            set { _fiberSynths = value; RaisePropertyChangedAuto(); }
        }

        private ObservableCollection<SpoolType> _spoolTypes;
        public ObservableCollection<SpoolType> SpoolTypes
        {
            get { return _spoolTypes; }
            set { _spoolTypes = value; RaisePropertyChangedAuto(); }
        }

        private Rml _selectedRML;
        public Rml SelectedRML
        {
            get { return _selectedRML; }
            set { _selectedRML = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
        }

        private Rml _activeRML;
        public Rml ActiveRML
        {
            get { return _activeRML; }
            set { _activeRML = value; RaisePropertyChangedAuto(); }
        }

        private CalibrationDataViewVM _calibrationDataViewVM;
        public CalibrationDataViewVM CalibrationDataViewVM
        {
            get { return _calibrationDataViewVM; }
            set { _calibrationDataViewVM = value; RaisePropertyChangedAuto(); }
        }

        private ObservableCollection<LiquidTypesRml> _liquidTypesRmls;
        public ObservableCollection<LiquidTypesRml> LiquidTypesRmls
        {
            get { return _liquidTypesRmls; }
            set { _liquidTypesRmls = value; RaisePropertyChangedAuto(); }
        }

        private ProcessParametersTablesGroup _activeProcessParametersGroup;
        public ProcessParametersTablesGroup ActiveProcessParametersGroup
        {
            get { return _activeProcessParametersGroup; }
            set { _activeProcessParametersGroup = value; RaisePropertyChangedAuto(); }
        }

        private ICollectionView _activeProcessParametersTableView;
        public ICollectionView ActiveProcessParametersTableView
        {
            get { return _activeProcessParametersTableView; }
            set { _activeProcessParametersTableView = value; RaisePropertyChangedAuto(); }
        }

        private ObservableCollection<CctModel> _ccts;
        public ObservableCollection<CctModel> CCTS
        {
            get { return _ccts; }
            set { _ccts = value; RaisePropertyChangedAuto(); }
        }

        private CctModel _selectedCCT;
        public CctModel SelectedCCT
        {
            get { return _selectedCCT; }
            set { _selectedCCT = value; RaisePropertyChangedAuto(); OnSelectedCCTChanged(); InvalidateRelayCommands(); }
        }

        private ColorConversionViewVM _colorConversionViewVM;
        public ColorConversionViewVM ColorConversionViewVM
        {
            get { return _colorConversionViewVM; }
            set { _colorConversionViewVM = value; RaisePropertyChangedAuto(); }
        }

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

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

        private String _RMLFilter;
        /// <summary>
        /// Gets or sets the job filter.
        /// </summary>
        public String RMLFilter
        {
            get { return _RMLFilter; }
            set { _RMLFilter = value; RaisePropertyChangedAuto(); OnRMLFilterChanged(); }
        }

        /// <summary>
        /// Gets or sets the manage RML command.
        /// </summary>
        public RelayCommand ManageRmlCommand { get; set; }

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

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

        public RelayCommand ImportForwardDataCommand { get; set; }

        public RelayCommand ExportForwardDataCommand { get; set; }

        public RelayCommand AddProcessParametersTableCommand { get; set; }

        public RelayCommand<ProcessParametersTable> RemoveProcessParametersTableCommand { get; set; }

        public RelayCommand AddLiquidFactorCommand { get; set; }

        public RelayCommand<LiquidTypesRml> RemoveLiquidFactorCommand { get; set; }

        public RelayCommand CreateCalibrationDataExcelTemplateCommand { get; set; }

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

        public RelayCommand SaveCommand { get; set; }

        public RelayCommand CloneRmlCommand { get; set; }

        public RelayCommand ExportRMLFileCommand { get; set; }

        public RelayCommand ImportRMLFileCommand { 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; }

        public MainViewVM(INotificationProvider notificationProvider, IAuthenticationProvider authentication, IActionLogManager actionLogManager)
        {
            _notification = notificationProvider;
            _authentication = authentication;
            _actionLogManager = actionLogManager;
            ManageRmlCommand = new RelayCommand(() => LoadActiveRML(SelectedRML.Guid), () => SelectedRML != null);
            RemoveRmlCommand = new RelayCommand(RemoveSelectedRml, () => SelectedRML != null);
            CloneRmlCommand = new RelayCommand(CloneSelectedRml, () => SelectedRML != null);
            AddRmlCommand = new RelayCommand(AddNewRml);
            BackToRmlsCommand = new RelayCommand(BackToRmls, () => IsFree);
            AddProcessParametersTableCommand = new RelayCommand(AddProcessParametersTable, () => IsFree);
            RemoveProcessParametersTableCommand = new RelayCommand<ProcessParametersTable>(RemoveProcessParametersTable, () => IsFree);
            AddLiquidFactorCommand = new RelayCommand(AddLiquidFactor, () => IsFree);
            RemoveLiquidFactorCommand = new RelayCommand<LiquidTypesRml>(RemoveLiquidFactor, () => IsFree);
            CreateCalibrationDataExcelTemplateCommand = new RelayCommand(CreateCalibrationDataExcelTemplate);
            SaveCommand = new RelayCommand(Save, () => IsFree);

            ImportForwardDataCommand = new RelayCommand(ImportCCTData, () => ActiveRML != null && IsFree);

            ExportForwardDataCommand = new RelayCommand(ExportCCTData, () => ActiveRML != null && SelectedCCT != null && IsFree);

            ExportRMLFileCommand = new RelayCommand(ExportRmlFile, () => SelectedRML != null && IsFree);
            ImportRMLFileCommand = new RelayCommand(ImportRmlFile, () => IsFree);

            AddSpoolCommand = new RelayCommand(AddNewSpool);
            RemoveSpoolCommand = new RelayCommand(RemoveSpool, () => SelectedSpool != null);
        }

        public override void OnApplicationReady()
        {
            LoadRmls();
        }

        private async void LoadRmls()
        {
            try
            {
                IsFree = false;

                using (_notification.PushTaskItem("Loading Rmls..."))
                {
                    if (_rmls_context != null) _rmls_context.Dispose();

                    _rmls_context = ObservablesContext.CreateDefault();
                    Rmls = await new RmlsCollectionBuilder(_rmls_context).SetAll().WithLiquidFactors().WithMediaProperties().BuildAsync();
                    //Load CCT file names...
                    var ccts = await _rmls_context.Ccts.Select(x => new
                    {
                        x.Guid,
                        x.FileName
                    }).ToListAsync();

                    foreach (var rml in Rmls)
                    {
                        var cct = ccts.SingleOrDefault(x => x.Guid == rml.CctGuid);

                        if (cct != null)
                        {
                            rml.Cct = new Cct()
                            {
                                Guid = cct.Guid,
                                FileName = cct.FileName,
                            };
                        }
                    }
                    RmlsCollectionView = CollectionViewSource.GetDefaultView(Rmls);
                    RmlsCollectionView.SortDescriptions.Add(new SortDescription(nameof(Rml.LastUpdated), ListSortDirection.Descending));
                    //RmlsCollectionView.Filter = new Predicate<object>(FilterCollection);

                    RmlsCollectionView.Filter = (rml) =>
                    {
                        Rml r = rml as Rml;
                        return String.IsNullOrWhiteSpace(RMLFilter)
                        || r.Name.ToLower().Contains(RMLFilter.ToLower());
                    };

                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, $"Error loading RMLS.\n{ex.FlattenMessage()}");
            }
            finally
            {
                IsFree = true;
            }
        }

        private async void LoadActiveRML(String guid)
        {
            using (_notification.PushTaskItem("Loading RML..."))
            {
                try
                {
                    IsFree = false;

                    if (_active_context != null)
                    {
                        _active_context.Dispose();
                    }

                    _active_context = ObservablesContext.CreateDefault();

                    CCTS = _active_context.Ccts
                        .Select(x => new CctModel()
                        {
                            Guid = x.Guid,
                            FileName = x.FileName,

                        }).ToObservableCollection();

                    CCTS.Where(x => String.IsNullOrWhiteSpace(x.FileName)).ToList().ForEach(x => x.FileName = x.Guid);

                    LoadRmlProperties();

                    ActiveRML = await new RmlBuilder(_active_context)
                        .Set(guid)
                        .WithActiveParametersGroup()
                        .WithLiquidFactors()
                        .WithCCT()
                        .WithSpools()
                        .BuildAsync();

                    if (ActiveRML.Cct != null)
                    {
                        SelectedCCT = CCTS.SingleOrDefault(x => x.Guid == ActiveRML.Cct.Guid);
                    }

                    if (ActiveRML.ProcessParametersTablesGroups.ToList().Count == 0)
                    {
                        if (!_notification.ShowQuestion("Could not find any process group for the selected RML. Would you like to create one?"))
                        {
                            _notification.ShowError("Cannot load an RML with no process group.");
                            IsFree = true;
                            return;
                        }
                        else
                        {
                            ProcessParametersTablesGroup group = new ProcessParametersTablesGroup();
                            group.Name = "Active Group";
                            group.Active = true;
                            group.ProcessParametersTables.Add(new ProcessParametersTable()
                            {
                                Name = "Process Table 1",
                            });

                            group.Rml = ActiveRML;

                            _active_context.ProcessParametersTablesGroups.Add(group);
                            _active_context.ProcessParametersTables.Add(group.ProcessParametersTables[0]);
                            await _active_context.SaveChangesAsync();
                            LoadActiveRML(ActiveRML.Guid);
                            return;
                        }
                    }

                    ActiveProcessParametersGroup = ActiveRML.ProcessParametersTablesGroups.ToList().FirstOrDefault();
                    ActiveProcessParametersTableView = CollectionViewSource.GetDefaultView(ActiveProcessParametersGroup.ProcessParametersTables);
                    ActiveProcessParametersTableView.SortDescriptions.Add(new SortDescription(nameof(ProcessParametersTable.TableIndex), ListSortDirection.Ascending));

                    CalibrationDataViewVM = new CalibrationDataViewVM(_notification);
                    LiquidTypesRmls = ActiveRML.LiquidTypesRmls;

                    foreach (var liquidTypeRml in LiquidTypesRmls)
                    {
                        CalibrationDataVM catVM = new CalibrationDataVM(liquidTypeRml.LiquidType);

                        if (liquidTypeRml.DefaultCatData != null)
                        {
                            catVM.CalibrationPoints = liquidTypeRml.GetCalibrationData().CalibrationPoints.Select(x => new CalibrationDataPointVM(x.X, x.Y)).ToObservableCollection();
                        }

                        CalibrationDataViewVM.LiquidsCalibrationData.Add(catVM);
                    }

                    ColorConversionViewVM = new ColorConversionViewVM(_notification)
                    {
                        RML = ActiveRML,
                        CCT = SelectedCCT,
                        LiquidsCalibrationData = CalibrationDataViewVM.LiquidsCalibrationData,
                        LiquidTypesRmls = LiquidTypesRmls,
                    };

                    ColorCalibrationVM = new ColorCalibrationViewVM(_notification)
                    {
                        RML = ActiveRML,
                        LiquidTypes = LiquidTypesRmls.Where(x => x.LiquidType.HasPigment).ToList().Select(y => y.LiquidType).ToList(),
                    };

                    _rmlBeforeSave = RmlDTO.FromObservable(ActiveRML);

                    View.NavigateTo(RmlNavigationView.RmlView);

                    InvalidateRelayCommands();

                    IsFree = true;
                }
                catch (Exception ex)
                {
                    LogManager.Log($"Error loading RML '{ActiveRML.Name}'...");
                    _notification.ShowError($"Error loading the selected thread.\n{ex.FlattenMessage()}");
                }
                finally
                {
                    IsFree = true;
                }
            }
        }

        private async void OnSelectedCCTChanged()
        {
            if (SelectedCCT != null && !SelectedCCT.IsNew)
            {
                using (_notification.PushTaskItem("Loading CCT data..."))
                {
                    IsFree = false;

                    var cct = await _active_context.Ccts.SingleOrDefaultAsync(x => x.Guid == SelectedCCT.Guid);

                    if (cct != null)
                    {
                        SelectedCCT.Data = cct.Data;
                        ColorConversionViewVM.CCT = SelectedCCT;
                    }

                    IsFree = true;
                }
            }
        }

        private void LoadRmlProperties()
        {
            Materials = _active_context.MediaMaterials.ToObservableCollection();
            Purposes = _active_context.MediaPurposes.ToObservableCollection();
            Conditions = _active_context.MediaConditions.ToObservableCollection();
            LinearMassDensityUnits = _active_context.LinearMassDensityUnits.ToObservableCollection();
            FiberShapes = _active_context.FiberShapes.ToObservableCollection();
            FiberSynths = _active_context.FiberSynths.ToObservableCollection();
            SpoolTypes = _active_context.SpoolTypes.ToObservableCollection();
        }

        private async void AddNewRml()
        {
            var name = _notification.ShowTextInput("Please enter RML name", "Name");

            if (!String.IsNullOrWhiteSpace(name))
            {
                if (Rmls.ToList().Exists(x => x.Name == name))
                {
                    _notification.ShowError("The specified RML name already exists. Please select a different name.");
                    return;
                }

                using (_notification.PushTaskItem("Creating new RML..."))
                {
                    IsFree = false;

                    if (_active_context != null)
                    {
                        _active_context.Dispose();
                    }

                    _active_context = ObservablesContext.CreateDefault();

                    LoadRmlProperties();

                    Rml rml = new Rml();
                    rml.Name = name;
                    rml.DisplayName = name;
                    rml.QualificationDate = DateTime.UtcNow;
                    rml.Manufacturer = "Twine";
                    rml.Code = Rmls.Count > 0 ? Rmls.Max(x => x.Code) + 1 : 1;
                    rml.MediaMaterial = Materials.FirstOrDefault();
                    rml.MediaPurpose = Purposes.FirstOrDefault();
                    rml.MediaCondition = Conditions.FirstOrDefault();
                    rml.LinearMassDensityUnit = LinearMassDensityUnits.FirstOrDefault();
                    rml.FiberShape = FiberShapes.FirstOrDefault();
                    rml.FiberSynth = FiberSynths.FirstOrDefault();

                    ProcessParametersTablesGroup group = new ProcessParametersTablesGroup();
                    group.Name = "Active Group";
                    group.Active = true;
                    group.ProcessParametersTables.Add(new ProcessParametersTable()
                    {
                        Name = "Process Table 1",
                    });

                    group.Rml = rml;

                    _active_context.ProcessParametersTablesGroups.Add(group);
                    _active_context.ProcessParametersTables.Add(group.ProcessParametersTables[0]);
                    _active_context.Rmls.Add(rml);
                    await _active_context.SaveChangesAsync();

                    _actionLogManager.InsertLog(BL.Enumerations.ActionLogType.RmlCreated, _authentication.CurrentUser, rml.Name, rml, "Rml created using Machine Studio.");

                    LoadActiveRML(rml.Guid);

                    IsFree = true;
                }
            }
        }

        private async void RemoveSelectedRml()
        {
            if (_notification.ShowQuestion("Removing the selected thread will result in the loss of all related process parameters and default calibration data. Are you sure you want to delete the selected RML?"))
            {
                using (_notification.PushTaskItem("Removing RML..."))
                {
                    try
                    {
                        IsFree = false;

                        var rml_jobs = await _rmls_context.Jobs.Where(x => x.RmlGuid == SelectedRML.Guid).Include(x => x.Machine).OrderBy(x => x.Machine.SerialNumber).ToListAsync();

                        if (rml_jobs.Count > 0)
                        {
                            var vm = new RmlDeleteDialogViewVM(SelectedRML, Rmls.ToList(), rml_jobs.ToList());
                            _notification.ShowModalDialog<RmlDeleteDialogViewVM, RmlDeleteDialogView>(vm, (x) => { }, () => { });

                            if (!vm.DialogResult)
                            {
                                return;
                            }

                            //Perform actions...
                            using (var db = ObservablesContext.CreateDefault())
                            {
                                foreach (var jobAction in vm.JobsActions)
                                {
                                    if (jobAction.Action == RmlDeleteDialogViewVM.RmlDeleteJobAction.Delete)
                                    {
                                        _actionLogManager.InsertLog(BL.Enumerations.ActionLogType.JobDeleted, _authentication.CurrentUser.Guid, jobAction.Job.Name, jobAction.Job.Guid, $"Job deleted due to RML '{SelectedRML.Name}' being removed.");
                                        await jobAction.Job.DeleteCascadeAsync(db);
                                    }
                                    else if (jobAction.Action == RmlDeleteDialogViewVM.RmlDeleteJobAction.Change)
                                    {
                                        var job = await db.Jobs.SingleOrDefaultAsync(x => x.Guid == jobAction.Job.Guid);
                                        job.RmlGuid = jobAction.TargetRml.Guid;
                                        _actionLogManager.InsertLog(BL.Enumerations.ActionLogType.JobSaved, _authentication.CurrentUser.Guid, jobAction.Job.Name, jobAction.Job.Guid, $"Job RML changed to '{jobAction.TargetRml.Name}' due to RML '{SelectedRML.Name}' being removed.");
                                    }
                                }

                                await db.SaveChangesAsync();
                            }
                        }

                        await SelectedRML.DeleteCascadeAsync(_rmls_context);
                        _actionLogManager.InsertLog(BL.Enumerations.ActionLogType.RmlDeleted, _authentication.CurrentUser, SelectedRML.Name, SelectedRML, "RML deleted from Machine Studio.");

                        LoadRmls();
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, $"Error removing selected RML {SelectedRML?.Name}.");
                        _notification.ShowError($"An error occurred while trying to remove the selected RML.\n{ex.FlattenMessage()}");
                        LoadRmls();
                    }
                    finally
                    {
                        IsFree = true;
                    }
                }
            }
        }

        private async void CloneSelectedRml()
        {
            String name = _notification.ShowTextInput("Enter thread name", "thread name");

            if (!String.IsNullOrWhiteSpace(name))
            {
                using (_notification.PushTaskItem("Cloning thread..."))
                {
                    try
                    {
                        IsFree = false;

                        using (var context = ObservablesContext.CreateDefault())
                        {
                            var rml = await new RmlBuilder(context).Set(SelectedRML.Guid).WithActiveParametersGroup().WithLiquidFactors().WithSpools().BuildAsync();

                            Rml cloned = new Rml();
                            rml.MapPropertiesTo(cloned, MappingFlags.NoReferenceTypes);

                            cloned.Code = Rmls.Max(x => x.Code) + 1;
                            cloned.Guid = Guid.NewGuid().ToString();
                            cloned.ID = 0;
                            cloned.Name = name;
                            cloned.DisplayName = name;
                            cloned.RmlQualificationLevel = RmlQualifications.Provisional;
                            cloned.QualificationDate = DateTime.UtcNow;

                            ProcessParametersTablesGroup group = new ProcessParametersTablesGroup();
                            group.Name = rml.GetActiveProcessGroup().Name;
                            group.Active = true;

                            foreach (var p in rml.GetActiveProcessGroup().ProcessParametersTables)
                            {
                                var pc = new ProcessParametersTable();
                                p.MapPrimitivesTo(pc);
                                pc.Name = p.Name;

                                group.ProcessParametersTables.Add(pc);
                            }

                            cloned.ProcessParametersTablesGroups.Add(group);

                            foreach (var liquidFactor in rml.LiquidTypesRmls)
                            {
                                LiquidTypesRml l = new LiquidTypesRml();
                                l.DefaultCatData = liquidFactor.DefaultCatData;
                                l.LiquidType = liquidFactor.LiquidType;
                                l.MaxNlPerCm = liquidFactor.MaxNlPerCm;
                                cloned.LiquidTypesRmls.Add(l);
                            }

                            foreach (var spool in rml.RmlsSpools)
                            {
                                RmlsSpool s = new RmlsSpool();
                                spool.MapPropertiesTo(s, MappingFlags.ValueTypesOnly);
                                s.RmlGuid = cloned.Guid;
                                s.SpoolTypeGuid = spool.SpoolTypeGuid;
                                cloned.RmlsSpools.Add(s);
                            }

                            context.Rmls.Add(cloned);
                            await context.SaveChangesAsync();

                            _actionLogManager.InsertLog(BL.Enumerations.ActionLogType.RmlCreated, _authentication.CurrentUser, cloned.Name, cloned, "RML cloned from Machine Studio.");
                        }

                        LoadRmls();
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, "Error cloning thread.");
                        _notification.ShowError($"An error occurred while trying to clone the selected thread\n{ex.Message}");
                    }
                    finally
                    {
                        IsFree = true;
                    }
                }

            }
        }

        private void AddProcessParametersTable()
        {
            var name = _notification.ShowTextInput("Enter table name", "Name");

            if (!String.IsNullOrWhiteSpace(name))
            {
                _active_context.ProcessParametersTables.Add(new ProcessParametersTable()
                {
                    ProcessParametersTablesGroup = ActiveProcessParametersGroup,
                    Name = name,
                    TableIndex = ActiveProcessParametersGroup.ProcessParametersTables.Max(x => x.TableIndex) + 1,
                });
            }
        }

        public void OnProcessParametersTableDropped(ProcessParametersTable dragged, ProcessParametersTable dropped)
        {
            if (dragged.TableIndex > dropped.TableIndex)
            {
                dragged.TableIndex = dropped.TableIndex - 1;
            }
            else
            {
                dragged.TableIndex = dropped.TableIndex + 1;
            }

            int index = 0;

            foreach (var table in ActiveProcessParametersGroup.ProcessParametersTables.OrderBy(x => x.TableIndex))
            {
                table.TableIndex = index++;
            }

            ActiveProcessParametersTableView.Refresh();
        }

        private void OnRMLFilterChanged()
        {
            RmlsCollectionView.Refresh();
        }

        private void RemoveLiquidFactor(LiquidTypesRml liquidFactor)
        {
            if (_notification.ShowQuestion("Removing this liquid factor will remove the liquid type association with the RML and will drop the calibration data. Are you sure?"))
            {
                var catVM = CalibrationDataViewVM.LiquidsCalibrationData.SingleOrDefault(x => x.LiquidType == liquidFactor.LiquidType);
                CalibrationDataViewVM.LiquidsCalibrationData.Remove(catVM);
                _active_context.LiquidTypesRmls.Remove(liquidFactor);
            }
        }

        private void AddLiquidFactor()
        {
            AddLiquidFactorViewVM vm = new AddLiquidFactorViewVM(_active_context);
            _notification.ShowModalDialog<AddLiquidFactorViewVM, AddLiquidFactorView>(vm, (_) =>
            {
                if (LiquidTypesRmls.ToList().Exists(x => x.LiquidType == vm.SelectedLiquidType))
                {
                    _notification.ShowError("The selected liquid type is already associated with this RML.");
                    return;
                }

                LiquidTypesRml liquidFactor = new LiquidTypesRml()
                {
                    LiquidType = vm.SelectedLiquidType,
                    Rml = ActiveRML,
                };

                _active_context.LiquidTypesRmls.Add(liquidFactor);

                CalibrationDataVM catVM = new CalibrationDataVM(liquidFactor.LiquidType);

                CalibrationDataViewVM.LiquidsCalibrationData.Add(catVM);

            }, () => { });
        }

        private void RemoveProcessParametersTable(ProcessParametersTable processParametersTable)
        {
            if (ActiveProcessParametersGroup.ProcessParametersTables.Count == 1)
            {
                _notification.ShowError("The process group must contain at least one table.");
                return;
            }

            if (_notification.ShowQuestion("Are you sure you want to remove this process parameters table?"))
            {
                _active_context.ProcessParametersTables.Remove(processParametersTable);
            }
        }

        private void CreateCalibrationDataExcelTemplate()
        {
            SaveFileDialog dlg = new SaveFileDialog();
            try
            {
                dlg.Title = $"Create excel template file";
                dlg.Filter = "Excel Files|*.xlsx";
                dlg.DefaultExt = ".xlsx";
                dlg.FileName = "Calibration File Template";
                if (dlg.ShowDialog().Value)
                {
                    CalibrationHelper.CreateCalibrationDataExcelTemplate(dlg.FileName);
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error generating excel calibration template file " + dlg.FileName);
                _notification.ShowError("An error occurred while trying to generate the calibration file.");
            }
        }

        private async void Save()
        {
            IsFree = false;

            try
            {
                using (_notification.PushTaskItem("Saving RML..."))
                {
                    foreach (var calDataVM in CalibrationDataViewVM.LiquidsCalibrationData)
                    {
                        var liquidTypeRml = LiquidTypesRmls.SingleOrDefault(x => x.LiquidType == calDataVM.LiquidType);
                        CalibrationData calData = new CalibrationData();
                        calData.LiquidType = (PMR.ColorLab.LiquidType)liquidTypeRml.LiquidType.Code;
                        calData.CalibrationPoints.AddRange(calDataVM.CalibrationPoints.Select(x => new CalibrationPoint() { X = x.X, Y = x.Y }));
                        liquidTypeRml.PutCalibrationData(calData);
                    }

                    ActiveRML.LastUpdated = DateTime.UtcNow;

                    if (_rmlBeforeSave.QualificationLevel != ActiveRML.QualificationLevel)
                    {
                        ActiveRML.QualificationDate = DateTime.UtcNow;
                    }

                    if (SelectedCCT != null)
                    {
                        if (SelectedCCT.IsNew)
                        {
                            Cct cct = new Cct();
                            cct.Guid = SelectedCCT.Guid;
                            cct.FileName = SelectedCCT.FileName;
                            cct.Data = SelectedCCT.Data;
                            ActiveRML.Cct = cct;
                            SelectedCCT.IsNew = false;
                        }
                        else
                        {
                            ActiveRML.CctGuid = SelectedCCT.Guid;
                        }
                    }

                    var rmlAfter = RmlDTO.FromObservable(ActiveRML);

                    await _active_context.SaveChangesAsync();

                    _actionLogManager.InsertLog(BL.Enumerations.ActionLogType.RmlSaved, _authentication.CurrentUser, _rmlBeforeSave.Name, _rmlBeforeSave, rmlAfter, "RML saved using Machine Studio.");

                    _rmlBeforeSave = rmlAfter;

                    LoadActiveRML(ActiveRML.Guid);
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, $"Error saving RML {ActiveRML.Name}");
                _notification.ShowError($"An error occurred while trying to save the current RML.\n{ex.FlattenMessage()}");
            }
            finally
            {
                IsFree = true;
            }
        }

        private void BackToRmls()
        {
            View.NavigateTo(RmlNavigationView.RmlsView);
            LoadRmls();
        }

        #region Import / Export Color Conversion Data

        private void ImportCCTData()
        {
            String file = GetCCTFileOpen();
            if (file != null)
            {
                if (CCTS.ToList().Exists(x => x.FileName == Path.GetFileName(file)))
                {
                    _notification.ShowError("The selected CCT file already exists on the database. Please select the CCT file from the dropdown box.");
                    return;
                }

                CctModel cctModel = new CctModel();
                cctModel.Guid = Guid.NewGuid().ToString();
                cctModel.IsNew = true;

                cctModel.FileName = Path.GetFileName(file);
                cctModel.Data = File.ReadAllBytes(file);

                CCTS.Insert(0, cctModel);
                SelectedCCT = cctModel;

                ColorConversionViewVM.CCT = SelectedCCT;
            }
        }

        private void ExportCCTData()
        {
            if (SelectedCCT != null)
            {
                String file = GetCCTFileSave(ActiveRML.Cct.FileName);
                if (file != null)
                {
                    using (_notification.PushTaskItem("Exporting CCT file..."))
                    {
                        try
                        {
                            IsFree = false;

                            if (SelectedCCT.IsNew)
                            {
                                File.WriteAllBytes(file, SelectedCCT.Data);
                            }
                            else
                            {
                                var cct = _active_context.Ccts.SingleOrDefault(x => x.Guid == SelectedCCT.Guid);

                                if (cct != null)
                                {
                                    File.WriteAllBytes(file, cct.Data);
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            LogManager.Log(ex, "Error exporting CCT file.");
                            _notification.ShowError($"An error occurred while trying to export the CCT file.\n{ex.Message}");
                        }
                        finally
                        {
                            IsFree = true;
                        }
                    }
                }
            }
        }

        private String GetCCTFileOpen()
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Title = "Select color adjustment file";
            dlg.Filter = "Color Conversion Table|*.cct";
            if (dlg.ShowDialogCenter())
            {
                return dlg.FileName;
            }

            return null;
        }

        private String GetCCTFileSave(String fileName)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Title = "Select color adjustment file";
            dlg.Filter = "Color Conversion Table|*.cct";
            dlg.FileName = fileName;
            dlg.DefaultExt = ".cct";
            if (dlg.ShowDialogCenter())
            {
                return dlg.FileName;
            }

            return null;
        }

        #endregion

        #region RML Import / Export

        private async void ImportRmlFile()
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Title = "Import Thread Files";
            dlg.Filter = "Twine Thread Files|*.rml";
            dlg.Multiselect = true;

            if (dlg.ShowDialog().Value)
            {
                using (_notification.PushTaskItem($"Importing thread files..."))
                {
                    try
                    {
                        IsFree = false;

                        LogManager.Log($"Importing thread files...");

                        using (ObservablesContext db = ObservablesContext.CreateDefault())
                        {
                            foreach (var file in dlg.FileNames)
                            {
                                var json = File.ReadAllText(file);
                                var rmlFile = await Rml.FromRmlFile(db, json);

                                db.Rmls.Add(rmlFile);

                                _actionLogManager.InsertLog(BL.Enumerations.ActionLogType.RmlImported, _authentication.CurrentUser, rmlFile.Name, rmlFile, "RML imported from Machine Studio.");
                            }

                            await db.SaveChangesAsync();
                        }

                        IsFree = true;

                        _notification.ShowInfo($"Threads imported successfully.");

                        LoadRmls();
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, "Error importing thread file.");
                        _notification.ShowError($"An error occurred while trying to import the selected thread file.\n{ex.FlattenMessage()}");
                    }
                    finally
                    {
                        IsFree = true;
                    }
                }
            }
        }

        private async void ExportRmlFile()
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Title = "Export Thread File";
            dlg.Filter = "Twine Thread Files|*.rml";
            dlg.DefaultExt = ".rml";
            dlg.FileName = SelectedRML.Name;

            if (dlg.ShowDialog().Value)
            {
                using (_notification.PushTaskItem($"Exporting Thread '{SelectedRML.Name}'..."))
                {
                    try
                    {
                        LogManager.Log($"Exporting Thread file {SelectedRML.Name}");

                        using (ObservablesContext db = ObservablesContext.CreateDefault())
                        {
                            var rmlJsonFile = await SelectedRML.ToRmlFile(db);
                            File.WriteAllText(dlg.FileName, rmlJsonFile);
                        }
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, "Error exporting Thread file.");
                        _notification.ShowError($"An error occurred while trying to export thread '{SelectedRML.Name}'.\n{ex.FlattenMessage()}");
                    }
                }
            }
        }

        #endregion

        #region Spools

        private void AddNewSpool()
        {
            _active_context.RmlsSpools.Add(new RmlsSpool()
            {
                Rml = ActiveRML,
            });
        }

        private void RemoveSpool()
        {
            if (SelectedSpool != null)
            {
                _active_context.RmlsSpools.Remove(SelectedSpool);
                ActiveRML.RmlsSpools.Remove(SelectedSpool);
            }
        }

        #endregion
    }
}