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

namespace Tango.Protobuf.CLI
{
    class Program
    {
        static int Main(string[] args)
        {
            try
            {
                Console.Title = "Tango Protobuf Compiler";

#if DEBUG

                if (args.Length == 0)
                {
                    args = new string[]
                    {
                        "-i " + Path.GetFullPath("..\\..\\..\\..\\PMR\\Messages"),
                        "-o " + Path.GetFullPath("..\\..\\..\\Tango.PMR"),
                        "-l CSharp",
                    };
                }

#endif

                if (args.Length == 0)
                {
                    return ExitHelp();
                }

                var options = new Options();

                if (CommandLine.Parser.Default.ParseArguments(args, options))
                {
                    options.SourceFolder = options.SourceFolder.Trim();
                    options.OutputFolder = options.OutputFolder.Trim();
                    options.Language = options.Language.Trim();

                    if (!Directory.Exists(options.SourceFolder))
                    {
                        return ExitError("Could not locate source folder \"" + Path.GetFullPath(options.SourceFolder) + "\"");
                    }
                    if (!Directory.Exists(options.OutputFolder))
                    {
                        return ExitError("Could not locate output folder " + Path.GetFullPath(options.OutputFolder));
                    }

                    CompilerLanguage language;
                    if (!Enum.TryParse<CompilerLanguage>(options.Language, out language))
                    {
                        return ExitError("Invalid language: " + options.Language);
                    }

                    try
                    {
                        var compiler = CompilerFactory.CreateCompiler(language);

                        compiler.CompilationProgress += (x, e) =>
                        {
                            try
                            {
                                Console.SetCursorPosition(0, 0);
                                Console.Write(new string(' ', Console.WindowWidth));
                                Console.SetCursorPosition(0, 0);
                                Console.Write("Compiling " + Path.GetFileName(e.File) + "...");
                                Console.SetCursorPosition(Console.WindowWidth - 7, 0);
                                Console.Write("(% " + Math.Round((((double)e.Current / (double)e.Total) * 100d), 0) + ")");
                            }
                            catch { }
                        };

                        var result = compiler.CompileFolder(Path.GetFullPath(options.SourceFolder), options.Includes != null ? options.Includes.Split(',') : null);
                        result.Save(Path.GetFullPath(options.OutputFolder));
                    }
                    catch (Exception ex)
                    {
                        return ExitError(ex.ToString());
                    }
                }

                return ExitSuccess("Protobuf folder compiled to " + Path.GetFullPath(options.OutputFolder));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return -1;
            }
        }

        private static int ExitSuccess(String text)
        {
            Console.WriteLine(text);
            return 0;
        }

        private static int ExitError(String error)
        {
            Console.WriteLine(error);
            return -1;
        }

        private static int ExitHelp()
        {
            Console.WriteLine(
@"
    Example: proto-tc -i <source folder> -o <output folder> -l <language>

    Available -l arguments:
    
        CSharp
        Java
        JavaNano
        CPP
        C
        EmbeddedC
        JS
        Python
        PHP
        Ruby
");

            return -1;
        }
    }
}
98 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
using LiveCharts;
using LiveCharts.Wpf;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using Tango.BL;
using Tango.BL.DTO;
using Tango.BL.Entities;
using Tango.BL.Enumerations;
using Tango.BL.FineTuning;
using Tango.Core;
using Tango.Core.Commands;
using Tango.Core.ExtensionMethods;
using Tango.CSV;
using Tango.FSE.Common;
using Tango.FSE.Common.AutoComplete;
using Tango.FSE.Common.Converters;
using Tango.FSE.Common.Navigation;
using Tango.FSE.Common.Notifications;
using Tango.FSE.Common.Statistics;
using Tango.FSE.Statistics.Dialogs;
using Tango.FSE.Statistics.Models;
using Tango.PPC.Shared.Statistics;
using Tango.SharedUI.Components;
using Tango.SharedUI.Helpers;

namespace Tango.FSE.Statistics.ViewModels
{
    public class MainViewVM : FSEViewModel
    {
        private const int NULL_JOB_INDEX = -1000;
        private int _currentJobIndex;
        private StatisticsModel _model;
        private String _currentFineTuningSessionID;
        private List<StopModel> _fineTuningStops;
        private List<LiquidType> _liquidTypes;

        #region Properties

        private DateTime _startSelectedDate;
        public DateTime StartSelectedDate
        {
            get { return _startSelectedDate; }
            set
            {
                _startSelectedDate = value;
                RaisePropertyChangedAuto();
            }
        }

        private DateTime _endSelectedDate;
        public DateTime EndSelectedDate
        {
            get { return _endSelectedDate; }
            set { _endSelectedDate = value; RaisePropertyChangedAuto(); }
        }

        protected Double _lengthLowerValue;
        public Double LengthLowerValue
        {
            get { return _lengthLowerValue; }
            set
            {
                _lengthLowerValue = value;
                RaisePropertyChangedAuto();
            }
        }

        protected Double _lengthUpperValue;
        public Double LengthUpperValue
        {
            get { return _lengthUpperValue; }
            set
            {
                _lengthUpperValue = value;
                RaisePropertyChangedAuto();
            }
        }

        private RequiredFiltersData _filtersData;
        public RequiredFiltersData FiltersData
        {
            get { return _filtersData; }
            set { _filtersData = value; RaisePropertyChangedAuto(); }
        }

        private SelectedObjectCollection<JobRunStatus> _jobRunSelectedStatuses;
        public SelectedObjectCollection<JobRunStatus> JobRunSelectedStatuses
        {
            get { return _jobRunSelectedStatuses; }
            set { _jobRunSelectedStatuses = value; RaisePropertyChangedAuto(); }
        }

        private SelectedObjectCollection<JobDesignations> _jobRunSelectedDesignations;
        public SelectedObjectCollection<JobDesignations> JobRunSelectedDesignations
        {
            get { return _jobRunSelectedDesignations; }
            set { _jobRunSelectedDesignations = value; RaisePropertyChangedAuto(); }
        }

        private SelectedObjectCollection<ThreadFilterData> _selectedThreads;
        public SelectedObjectCollection<ThreadFilterData> SelectedThreads
        {
            get { return _selectedThreads; }
            set { _selectedThreads = value; RaisePropertyChangedAuto(); }
        }

        private String _jobName;
        public String JobName
        {
            get { return _jobName; }
            set { _jobName = value; RaisePropertyChangedAuto(); }
        }

        private bool _includeHeadCleaning;
        public bool IncludeHeadCleaning
        {
            get { return _includeHeadCleaning; }
            set { _includeHeadCleaning = value; RaisePropertyChangedAuto(); }
        }

        private bool _isSettingsBarOpened;
        public bool IsSettingsBarOpened
        {
            get { return _isSettingsBarOpened; }
            set { _isSettingsBarOpened = value; RaisePropertyChangedAuto(); }
        }

        private bool _isFiltersAvailable;
        public bool IsFiltersAvailable
        {
            get { return _isFiltersAvailable; }
            set { _isFiltersAvailable = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
        }

        private SynchronizedObservableCollection<StopModel> _stops;
        public SynchronizedObservableCollection<StopModel> Stops
        {
            get { return _stops; }
            set { _stops = value; RaisePropertyChangedAuto(); RaisePropertyChanged(nameof(IsResultsAvailable)); InvalidateRelayCommands(); }
        }

        private ListCollectionView _stopView;
        public ListCollectionView StopsView
        {
            get { return _stopView; }
            set { _stopView = value; RaisePropertyChangedAuto(); }
        }

        private bool _jobNameExact;
        public bool JobNameExact
        {
            get { return _jobNameExact; }
            set { _jobNameExact = value; RaisePropertyChangedAuto(); }
        }

        private StatsModel _stats;
        public StatsModel Stats
        {
            get { return _stats; }
            set { _stats = value; RaisePropertyChangedAuto(); }
        }

        public bool IsResultsAvailable
        {
            get { return Stops != null && Stops.Count > 0 && Stops[0].JobIndex != NULL_JOB_INDEX; }
        }

        private SeriesCollection _seriesCollection;
        public SeriesCollection SeriesCollection
        {
            get { return _seriesCollection; }
            set { _seriesCollection = value; RaisePropertyChangedAuto(); }
        }


        #endregion

        #region Commands

        public RelayCommand GetStatisticsCommand { get; set; }

        public RelayCommand<StopModel> ShowFailedMessageCommand { get; set; }

        public RelayCommand<StopModel> ShowExtendedInfoCommand { get; set; }

        public RelayCommand<StopModel> CopyCommand { get; set; }

        public RelayCommand ExportToCsvCommand { get; set; }

        public RelayCommand OpenStreamingSettingsCommand { get; set; }

        public RelayCommand ClearCommand { get; set; }

        #endregion

        #region Suggestions Providers

        public AutoCompleteSource<String> JobsAutoCompleteProvider { get; set; }

        #endregion

        #region Constructors

        public MainViewVM()
        {
            IsSettingsBarOpened = true;

            _model = new StatisticsModel();

            JobRunSelectedStatuses = new SelectedObjectCollection<JobRunStatus>(new ObservableCollection<JobRunStatus>()
            {
                JobRunStatus.Aborted,
                JobRunStatus.Completed,
                JobRunStatus.Failed,

            }, new ObservableCollection<JobRunStatus>()
            {
                JobRunStatus.Aborted,
                JobRunStatus.Completed,
                JobRunStatus.Failed,

            });

            JobRunSelectedDesignations = new SelectedObjectCollection<JobDesignations>(new ObservableCollection<JobDesignations>()
            {
                JobDesignations.Default,
                JobDesignations.FineTuning,

            }, new ObservableCollection<JobDesignations>()
            {
                JobDesignations.Default,
                JobDesignations.FineTuning,
            });


            JobRunSelectedStatuses.SelectionChanged -= (x, y) => RaisePropertyChanged(nameof(JobRunSelectedStatuses));
            JobRunSelectedStatuses.SelectionChanged += (x, y) => RaisePropertyChanged(nameof(JobRunSelectedStatuses));

            JobRunSelectedDesignations.SelectionChanged -= (x, y) => RaisePropertyChanged(nameof(JobRunSelectedDesignations));
            JobRunSelectedDesignations.SelectionChanged += (x, y) => RaisePropertyChanged(nameof(JobRunSelectedDesignations));

            JobsAutoCompleteProvider = new AutoCompleteSource<string>(AutoCompleteJobs);

            StartSelectedDate = DateTime.Now.AddDays(-7);
            EndSelectedDate = DateTime.Now;
            LengthUpperValue = 1000000;

            GetStatisticsCommand = new RelayCommand(GetStatistics, () => IsFiltersAvailable);

            //Just for showing the columns :/
            Stops = new List<StopModel>() { new StopModel() { JobRun = new JobRunDTO(), Job = new PresentationJob(), JobIndex = NULL_JOB_INDEX } }.ToSynchronizedObservableCollection();
            var view = new ListCollectionView(Stops);
            view.GroupDescriptions.Add(new PropertyGroupDescription("JobIndex"));
            view.GroupDescriptions.Add(new PropertyGroupDescription("SegmentIndex"));
            StopsView = view;

            Stats = new StatsModel();

            ShowFailedMessageCommand = new RelayCommand<StopModel>(ShowJobRunFailedMessage);
            ShowExtendedInfoCommand = new RelayCommand<StopModel>(ShowJobRunExtendedInfo);
            CopyCommand = new RelayCommand<StopModel>(CopyStopToClipboard);
            ExportToCsvCommand = new RelayCommand(ExportToCSV, () => IsResultsAvailable);
            OpenStreamingSettingsCommand = new RelayCommand(OpenStreamingSettings);
            ClearCommand = new RelayCommand(Clear);
        }

        #endregion

        #region Override Methods

        public override void OnApplicationStarted()
        {
            InvokeUI(() =>
            {
                NavigationManager.MenuItems.Add(new NavigationMenuItem(() =>
                {
                    NavigationManager.NavigateTo<StatisticsModule>();
                })
                {
                    Name = "Statistics",
                    Index = 11,
                    Description = "Query for statistical data of a machine.",
                    Image = ResourceHelper.GetImageFromResources("Images/statistics.png"),
                });
            });
        }

        public override void OnApplicationReady()
        {
            base.OnApplicationReady();

            MachineProvider.MachineConnected += MachineProvider_MachineConnected;
            MachineProvider.MachineDisconnected += MachineProvider_MachineDisconnected;

            StatisticsProvider.JobRunReceived += StatisticsProvider_JobRunReceived;
        }

        #endregion

        #region Machine Connection / Disconnection

        private void MachineProvider_MachineDisconnected(object sender, Common.Connection.MachineDisconnectedEventArgs e)
        {

        }

        private async void MachineProvider_MachineConnected(object sender, Common.Connection.MachineConnectedEventArgs e)
        {
            if (MachineProvider.IsPPCAvailable && (e.DifferentFromPrevious || !IsFiltersAvailable))
            {
                await LoadFiltersData();
            }
        }

        #endregion

        #region Statistics Methods

        private async Task LoadFiltersData()
        {
            try
            {
                FiltersData = await StatisticsProvider.GetRequiredFiltersData();
                SelectedThreads = new SelectedObjectCollection<ThreadFilterData>(FiltersData.Rmls.ToObservableCollection(), new ObservableCollection<ThreadFilterData>());
                IsFiltersAvailable = true;
            }
            catch (Exception ex)
            {
                IsFiltersAvailable = false;
                NotificationProvider.PushErrorReportingSnackbar(ex, "Statistics Module Error", "Error loading required statistics filter data.");
            }
        }

        private async void GetStatistics()
        {
            try
            {
                using (var task = NotificationProvider.PushTaskItem("Getting statistics from the remote machine...", false))
                {
                    await Task.Delay(3000);

                    Filters filters = new Filters();
                    filters.StartDateUTC = StartSelectedDate;
                    filters.EndDateUTC = EndSelectedDate;
                    filters.EndStatuses = JobRunSelectedStatuses.SynchedSource.Cast<int>().ToList();
                    filters.JobDesignation = JobRunSelectedDesignations.SynchedSource.Cast<int>().ToList();
                    filters.IncludeHeadCleaning = IncludeHeadCleaning;
                    filters.JobName = JobName;
                    filters.ExactJobName = JobNameExact;
                    filters.MinLength = (int)LengthLowerValue;
                    filters.MaxLength = (int)LengthUpperValue;
                    filters.RmlGuids = SelectedThreads.SynchedSource.Select(x => x.Guid).ToList();

                    _model = await StatisticsProvider.GetStatistics(filters);
                    _liquidTypes = _model.StatisticsResult.LiquidTypes.Select(x => x.ToObservable()).ToList();

                    List<StopModel> stops = new List<StopModel>();

                    _currentJobIndex = 0;
                    _currentFineTuningSessionID = null;
                    _fineTuningStops = new List<StopModel>();

                    foreach (var job in _model.StatisticsResult.JobRuns.OrderByDescending(x => x.JobRun.StartDate))
                    {
                        stops.AddRange(CreateJobRunStops(job));
                    }

                    Stops = stops.ToSynchronizedObservableCollection();

                    var view = new ListCollectionView(Stops);
                    view.GroupDescriptions.Add(new PropertyGroupDescription("JobIndex"));
                    view.GroupDescriptions.Add(new PropertyGroupDescription("SegmentIndex"));

                    StopsView = view;

                    UpdateSummaries();

                    if (Stops.Count > 0)
                    {
                        await Task.Delay(1000);

                        IsSettingsBarOpened = false;

                        await Task.Delay(1000);
                    }
                }
            }
            catch (Exception ex)
            {
                NotificationProvider.PushErrorReportingSnackbar(ex, "Statistics Module Error", "Error getting statistics from the remote machine.");
            }
        }

        private List<StopModel> CreateJobRunStops(JobRunComposition job)
        {
            List<StopModel> stops = new List<StopModel>();

            var rmlName = FiltersData.Rmls.FirstOrDefault(x => x.Guid == job.JobRun.RmlGuid)?.Name;

            VectorFineTuningRunModel fineTuningModel = null;

            if (job.JobRun.JobDesignation == (int)JobDesignations.FineTuning && job.JobRun.FineTuningString != null)
            {
                fineTuningModel = JsonConvert.DeserializeObject<VectorFineTuningRunModel>(job.JobRun.FineTuningString);
                if (fineTuningModel.FineTuningSessionID != _currentFineTuningSessionID)
                {
                    _fineTuningStops = new List<StopModel>();
                    _currentFineTuningSessionID = fineTuningModel.FineTuningSessionID;
                    _currentJobIndex++;
                }
            }
            else
            {
                _currentJobIndex++;
            }

            foreach (var jobSegment in job.Job.Segments)
            {
                foreach (var jobStop in jobSegment.Stops)
                {
                    StopModel stop = new StopModel();
                    stop.Index = jobSegment.Stops.IndexOf(jobStop) + 1;
                    stop.JobIndex = _currentJobIndex;
                    stop.SegmentIndex = job.Job.Segments.IndexOf(jobSegment) + 1;
                    stop.ThreadName = rmlName;
                    stop.IsAdvancedMode = !BuildProvider.IsTwineRSM && CurrentUser.HasRole(Roles.FSEAdvancedTechnician);
                    stop.FineTuningModel = fineTuningModel;
                    stop.MachineType = MachineProvider.MachineOperator.MachineType;

                    if (fineTuningModel != null)
                    {
                        stop.IsFineTuningApproved = fineTuningModel.Approved;
                        stop.IsFineTuningSelected = fineTuningModel.Approved;

                        _fineTuningStops.Add(stop);

                        if (_fineTuningStops.Exists(x => x.IsFineTuningApproved))
                        {
                            _fineTuningStops.ForEach(x => x.IsFineTuningApproved = true);
                        }
                    }

                    stop.ColorSpace = jobStop.ColorSpace;

                    stop.L = jobStop.L;
                    stop.A = jobStop.A;
                    stop.B = jobStop.B;

                    stop.Red = jobStop.Red;
                    stop.Green = jobStop.Green;
                    stop.Blue = jobStop.Blue;

                    stop.Catalog = jobStop.Catalog;
                    stop.CatalogItem = jobStop.CatalogItem;

                    stop.StartMeters = jobStop.StartMeters;

                    stop.Job = job.Job;
                    stop.JobRun = job.JobRun;
                    stop.Segment = jobSegment;

                    stop.LiquidVolumes = jobStop.LiquidVolumes;
                    stop.BestMatchR = jobStop.BestMatchR;
                    stop.BestMatchG = jobStop.BestMatchG;
                    stop.BestMatchB = jobStop.BestMatchB;

                    stop.ShowFailedMessageCommand = ShowFailedMessageCommand;
                    stop.ShowExtendedInfoCommand = ShowExtendedInfoCommand;
                    stop.CopyCommand = CopyCommand;

                    stops.Add(stop);
                }
            }

            return stops;
        }

        private void UpdateSummaries()
        {
            var stats = new StatsModel();

            try
            {
                stats.TotalRuns = _model.StatisticsResult.JobRuns.Count;
                stats.CompletedRuns = _model.StatisticsResult.JobRuns.Count(x => x.JobRun.Status == (int)JobRunStatus.Completed);
                stats.FailedRuns = _model.StatisticsResult.JobRuns.Count(x => x.JobRun.Status == (int)JobRunStatus.Failed);
                stats.AbortedRuns = _model.StatisticsResult.JobRuns.Count(x => x.JobRun.Status == (int)JobRunStatus.Aborted);
                stats.TotalDyeingLength = (int)_model.StatisticsResult.JobRuns.Where(x => x.JobRun.EndPosition > 0).Select(x => x.Distance).Sum();
                stats.AverageDyeingLength = (int)_model.StatisticsResult.JobRuns.Where(x => x.JobRun.EndPosition > 0).Select(x => x.Distance).Average();

                var timeRuns = _model.StatisticsResult.JobRuns.Where(z => z.JobRun.EndPosition > 0 && z.JobRun.EndDate != null && z.JobRun.ActualStartDate != null).ToList();

                stats.TotalDyeingTime = TimeSpan.FromHours(timeRuns.Sum(x => (x.JobRun.EndDate - x.JobRun.ActualStartDate.Value).TotalHours));
                stats.AverageDyeingTime = TimeSpan.FromHours(timeRuns.Average(x => (x.JobRun.EndDate - x.JobRun.ActualStartDate.Value).TotalHours));

                SeriesCollection seriesCollection = new SeriesCollection();

                List<LiquidQuantityModel> liquidQuantities = new List<LiquidQuantityModel>();

                foreach (var liquidType in _liquidTypes.ToList())
                {
                    liquidQuantities.Add(new LiquidQuantityModel()
                    {
                        LiquidType = liquidType,
                        Quantity = 0
                    });
                }

                foreach (var stop in Stops)
                {
                    foreach (var quantity in liquidQuantities)
                    {
                        quantity.Quantity += (double)typeof(JobRun).GetProperty(quantity.LiquidType.Type.ToString() + "Quantity").GetValue(stop.JobRun);
                    }
                }

                var foreground = Application.Current.Resources["FSE_PrimaryForegroundBrush"] as SolidColorBrush;
                var border = Application.Current.Resources["FSE_BorderBrush"] as SolidColorBrush;

                foreach (var liquidQuantity in liquidQuantities)
                {
                    PieSeries series = new PieSeries();
                    series.Foreground = foreground;
                    series.Stroke = border;
                    series.Fill = liquidQuantity.LiquidType.LiquidTypeBrush;
                    series.FontSize = 12;
                    series.Title = liquidQuantity.Title;
                    series.Values = new ChartValues<double>() { Convert.ToDouble(liquidQuantity.Quantity) };
                    series.DataLabels = false;
                    series.LabelPoint = Stats.QuantityCounts;

                    seriesCollection.Add(series);
                }

                SeriesCollection = seriesCollection;

                stats.LiquidQuantities = liquidQuantities;
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error generating statistics summaries.");
            }

            Stats = stats;
        }

        private List<String> AutoCompleteJobs(string key)
        {
            key = key ?? String.Empty;
            return FiltersData.Jobs.Where(x => x != null).Where(x => x.ToLower().Contains(key.ToLower())).ToList();
        }

        private void Clear()
        {
            Stops?.Clear();
            Stats = new StatsModel();
        }

        #endregion

        #region Stop Commands Handler

        private async void ShowJobRunFailedMessage(StopModel stop)
        {
            await NotificationProvider.ShowError($"Job Failure Message:\n{stop.JobRun.FailedMessage}");
        }

        private async void ShowJobRunExtendedInfo(StopModel stop)
        {
            if (stop.ExtendedInfo == null)
            {
                try
                {
                    using (var task = NotificationProvider.PushTaskItem($"Retrieving extended job run ({stop.JobRun.ID}) information...", false))
                    {
                        await Task.Delay(1000);
                        stop.ExtendedInfo = await StatisticsProvider.GetJobRunExtendedInfo(stop.JobRun.ID);
                    }
                }
                catch (Exception ex)
                {
                    await NotificationProvider.ShowError($"Could not retrieve extended job run information.\n{ex.Message}");
                    return;
                }
            }

            var vm = await NotificationProvider.ShowDialog<JobRunExtendedInfoViewVM>(new JobRunExtendedInfoViewVM() { ExtendedInfo = stop.ExtendedInfo });
            if (vm.Result == JobRunExtendedInfoViewVM.JobRunExtendedInfoDialogResult.ExportJson)
            {
                var result = await StorageProvider.SaveFile("Export job run extended information", "JSON Files|*.json", $"JobRun_{stop.JobRun.JobName}_{stop.JobRun.ID}_extended.json", ".json");
                if (result)
                {
                    File.WriteAllText(result.SelectedItem, vm.ExtendedInfo.ToJsonString());

                    NotificationProvider.PushSnackbarItem(MessageType.Success, "Job Run Extended Information Export", true, "Export completed successfully.\nTap to open the file.", null, null, async () =>
                    {
                        await StorageProvider.ShowInExplorer(result.SelectedItem);
                    });
                }
            }
            else if (vm.Result == JobRunExtendedInfoViewVM.JobRunExtendedInfoDialogResult.ExportCSV)
            {
                var result = await StorageProvider.SaveFile("Export job run job ticket", "CSV Files|*.csv", $"JobRun_{stop.JobRun.JobName}_{stop.JobRun.ID}_ticket.csv", ".csv");
                if (result)
                {
                    var dispensers = vm.ExtendedInfo.JobTicket.Segments.SelectMany(x => x.BrushStops).SelectMany(x => x.Dispensers).Select(x => new JobTicketCsvModel()
                    {
                        DispenserIndex = x.Index.ToString(),
                        JobName = stop.JobRun.JobName,
                        LiquidType = x.DispenserLiquidType.ToString(),
                        NanoliterPerCM = x.NanoliterPerCentimeter.ToString(),
                        NanoliterPerSec = x.NanolitterPerSecond.ToString(),
                        SegmentIndex = stop.SegmentIndex.ToString(),
                        StopIndex = stop.Index.ToString(),
                        Volume = x.Volume.ToString(),
                    }).ToList();

                    using (CsvFile<JobTicketCsvModel> csvFile = new CsvFile<JobTicketCsvModel>(new CsvDestination(result.SelectedItem)))
                    {
                        foreach (var dispenser in dispensers)
                        {
                            csvFile.Append(dispenser);
                        }
                    }

                    NotificationProvider.PushSnackbarItem(MessageType.Success, "Job Run Ticket Export", true, "Export completed successfully.\nTap to open the file.", null, null, async () =>
                    {
                        await StorageProvider.ShowInExplorer(result.SelectedItem);
                    });
                }
            }
        }

        private void CopyStopToClipboard(StopModel stop)
        {
            String input = String.Empty;

            switch (stop.ColorSpace)
            {
                case ColorSpaces.RGB:
                    input = $"{stop.Red}\t{stop.Green}\t{stop.Blue}";
                    break;
                case ColorSpaces.LAB:
                    input = $"{stop.L}\t{stop.A}\t{stop.B}";
                    break;
                case ColorSpaces.Catalog:
                    input = $"{stop.Catalog}\t{stop.CatalogItem}";
                    break;
                case ColorSpaces.Volume:
                    input = String.Join("\t", stop.GetVolumeInputs().Where(x => x.Volume > 0).Select(x => x.Volume));
                    break;
            }

            Clipboard.SetText($"{stop.ColorSpace}\t{input}\t{String.Join("\t", stop.Output.Where(x => x.LiquidType.HasPigment).Where(x => x.Volume > 0).Select(x => x.Volume))}", TextDataFormat.Text);
        }

        #endregion

        #region CSV Export

        private async void ExportToCSV()
        {
            var result = await StorageProvider.SaveFile("Export To CSV File", "CSV Files|*.csv", $"{MachineProvider.Machine.SerialNumber}_JobRuns_{DateTime.Now.ToFileName()}.csv", ".csv");
            if (result.Confirmed)
            {
                using (NotificationProvider.PushTaskItem("Exporting job runs to file..."))
                {
                    await Task.Delay(1000);

                    try
                    {
                        ExportToCSV(Stops.ToList(), result.SelectedItem);

                        NotificationProvider.PushSnackbarItem(MessageType.Success, "Statistics File Export", true, "Export completed successfully.\nTap to open the file.", null, null, async () =>
                        {
                            await StorageProvider.ShowInExplorer(result.SelectedItem);
                        });
                    }
                    catch (Exception ex)
                    {
                        LogManager.Log(ex, "Error exporting csv file.");
                        await NotificationProvider.ShowError($"Error exporting the csv file.\n{ex.FlattenMessage()}");
                    }
                }
            }
        }

        private void ExportToCSV(List<StopModel> stops, String filePath)
        {
            CsvFile<CsvModel> csvFile = new CsvFile<CsvModel>(new CsvDestination(filePath), new CsvDefinition()
            {
                Columns = new List<String>()
                         {
                             "Job Index",
                             "Job Name",
                             "Job Kind",
                             "Thread",
                             "Length",
                             "Number Of Units",
                             "Start Time",
                             "Duration",
                             "Distance",
                             "Start Position",
                             "End Position",
                             "Status",
                             "Segment Index",
                             "Offset",
                             "Color Space",
                             "Target L",
                             "Target A",
                             "Target B",
                             "Measured L",
                             "Measured A",
                             "Measured B",
                             "Delta E",
                             "Approved",
                             "Input RGB (R)",
                             "Input RGB (G)",
                             "Input RGB (B)",
                             "Input LAB (L)",
                             "Input LAB (A)",
                             "Input LAB (B)",
                             "Input Catalog",
                             "Input Catalog Item",
                             "Input Cyan",
                             "Input Magenta",
                             "Input Yellow",
                             "Input Black",
                             "Input Blue",
                             "Input Orange",
                             "Input Rubine",
                             "Input Navy",
                             "Input Violet",
                             "Output Cyan",
                             "Output Magenta",
                             "Output Yellow",
                             "Output Black",
                             "Output Light Cyan",
                             "Output Light Magenta",
                             "Output Light Yellow",
                             "Output Blue",
                             "Output Light Blue",
                             "Output Orange",
                             "Output Light Orange",
                             "Output Rubine",
                             "Output Light Rubine",
                             "Output Navy",
                             "Output Violet",
                             "Output Transparent Ink",
                             "Output Lubricant",
                             "Failure Reason",
                         },
            });

            foreach (var stop in stops.OrderBy(x => x.JobIndex).ToList())
            {
                CsvModel model = new CsvModel();
                model.JobIndex = stop.JobIndex.ToString();
                model.JobName = stop.JobRun.JobName;
                model.JobKind = ((JobDesignations)stop.JobRun.JobDesignation).ToDescription();
                model.Thread = stop.ThreadName;
                model.Length = stop.LogicalLengthMeters.ToString();
                model.NumberOfUnits = stop.JobRun.NumberOfUnits.ToString();
                model.StartTime = stop.JobRun.StartDate.ToLocalTime().ToString();
                model.Duration = stop.Duration.ToStringUnlimitedHours();
                model.Distance = stop.Distance.ToString();
                model.StartPosition = stop.ActualStartPosition.ToString();
                model.EndPosition = stop.ActualEndPosition.ToString();
                model.Status = ((JobRunStatus)stop.JobRun.Status).ToString();
                model.SegmentIndex = stop.SegmentIndex.ToString();
                model.Offset = stop.StartMeters.ToString();
                model.ColorSpace = stop.ColorSpace.ToString();

                switch (stop.ColorSpace)
                {
                    case ColorSpaces.RGB:
                        model.InputRed = stop.Red.ToString();
                        model.InputGreen = stop.Green.ToString();
                        model.InputBlue = stop.Blue.ToString();
                        break;
                    case ColorSpaces.LAB:
                        model.InputL = stop.L.ToString();
                        model.InputA = stop.A.ToString();
                        model.InputB = stop.B.ToString();
                        break;
                    case ColorSpaces.Catalog:
                        model.InputCatalog = stop.Catalog;
                        model.InputCatalogItem = stop.CatalogItem;
                        break;
                    case ColorSpaces.Volume:
                        model.InputCyan = stop.GetLiquidVolumeInputOrZero(LiquidTypes.Cyan).ToString();
                        model.InputMagenta = stop.GetLiquidVolumeInputOrZero(LiquidTypes.Magenta).ToString();
                        model.InputYellow = stop.GetLiquidVolumeInputOrZero(LiquidTypes.Yellow).ToString();
                        model.InputBlack = stop.GetLiquidVolumeInputOrZero(LiquidTypes.Black).ToString();

                        model.InputBBlue = stop.GetLiquidVolumeInputOrZero(LiquidTypes.Blue).ToString();
                        model.InputOrange = stop.GetLiquidVolumeInputOrZero(LiquidTypes.Orange).ToString();
                        model.InputRubine = stop.GetLiquidVolumeInputOrZero(LiquidTypes.Rubine).ToString();
                        model.InputNavy = stop.GetLiquidVolumeInputOrZero(LiquidTypes.Navy).ToString();
                        model.InputViolet = stop.GetLiquidVolumeInputOrZero(LiquidTypes.Violet).ToString();
                        break;
                }

                if (stop.JobRun.JobDesignation == (int)JobDesignations.FineTuning)
                {
                    if (stop.FineTuningModel != null)
                    {
                        model.TargetL = stop.FineTuningModel.FineTuningTargetL.ToString();
                        model.TargetA = stop.FineTuningModel.FineTuningTargetA.ToString();
                        model.TargetB = stop.FineTuningModel.FineTuningTargetB.ToString();

                        model.MeasuredL = stop.FineTuningModel.FineTuningMeasuredL.ToStringSafe();
                        model.MeasuredA = stop.FineTuningModel.FineTuningMeasuredA.ToStringSafe();
                        model.MeasuredB = stop.FineTuningModel.FineTuningMeasuredB.ToStringSafe();

                        model.DeltaE = stop.FineTuningDeltaE.ToStringSafe();
                        model.Approved = stop.FineTuningModel.Approved.ToStringYesNo();
                    }
                }

                model.OutputCyan = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.Cyan).ToString();
                model.OutputMagenta = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.Magenta).ToString();
                model.OutputYellow = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.Yellow).ToString();
                model.OutputBlack = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.Black).ToString();
                model.OutputLightCyan = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.LightCyan).ToString();
                model.OutputLightMagenta = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.LightMagenta).ToString();
                model.OutputLightYellow = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.LightYellow).ToString();

                model.OutputBlue = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.Blue).ToString();
                model.OutputLightBlue = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.LightBlue).ToString();
                model.OutputOrange = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.Orange).ToString();
                model.OutputLightOrange = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.LightOrange).ToString();
                model.OutputRubine = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.Rubine).ToString();
                model.OutputLightRubine = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.LightRubine).ToString();
                model.OutputNavy = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.Navy).ToString();
                model.OutputViolet = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.Violet).ToString();

                model.OutputTransparentInk = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.TransparentInk).ToString();
                model.OutputLubricant = stop.GetLiquidVolumeOutputOrZero(LiquidTypes.Lubricant).ToString();
                model.FailureReason = stop.JobRun.FailedMessage;

                csvFile.Append(model);
            }

            csvFile.Dispose();
        }

        #endregion

        #region Streaming

        private async void OpenStreamingSettings()
        {
            await NotificationProvider.ShowDialog<JobRunStreamingViewVM>();
            Settings.Save();
        }

        private void StatisticsProvider_JobRunReceived(object sender, JobRunReceivedEventArgs e)
        {
            var stops = CreateJobRunStops(e.JobRunComposition);

            stops.Reverse();

            InvokeUI(() =>
            {
                try
                {
                    if (Stops.Count == 1 && Stops[0].JobIndex == NULL_JOB_INDEX)
                    {
                        Stops.Clear();
                    }

                    if (stops.First().IsFineTuning && stops.First().FineTuningModel != null)
                    {
                        var sessionStop = Stops.Where(x => x.IsFineTuning).FirstOrDefault(x => x.FineTuningModel.FineTuningSessionID == stops[0].FineTuningModel.FineTuningSessionID);

                        if (sessionStop != null)
                        {
                            stops.First().JobIndex = sessionStop.JobIndex;
                            stops.First().IsFineTuningApproved = sessionStop.IsFineTuningApproved;
                        }
                    }

                    foreach (var stop in stops)
                    {
                        Stops.Insert(0, stop);
                    }

                    _model.StatisticsResult.JobRuns.Add(e.JobRunComposition);

                    UpdateSummaries();
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Statistics streaming error updating the job runs grid");
                }

                RaisePropertyChanged(nameof(IsResultsAvailable));
                InvalidateRelayCommands();
            });

            if (Settings.StatisticsStreamingConfig.EnableCsvReports)
            {
                try
                {
                    if (!Directory.Exists(Settings.StatisticsStreamingConfig.CsvReportsFolder))
                    {
                        Directory.CreateDirectory(Settings.StatisticsStreamingConfig.CsvReportsFolder);
                    }

                    stops.Reverse();

                    String suffix = "Run";

                    if (stops[0].JobRun.JobDesignation == (int)JobDesignations.FineTuning)
                    {
                        suffix = "VFT";
                    }

                    String fileName = $"{MachineProvider.Machine.SerialNumber}_{stops[0].JobRun.JobName}_{suffix}_{DateTime.Now.ToFileName()}.csv";
                    ExportToCSV(stops, Path.Combine(Settings.StatisticsStreamingConfig.CsvReportsFolder, fileName));
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Statistics streaming error exporting the job run csv report.");
                }
            }
        }

        #endregion
    }
}