aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/PropertiesTemplates/ControllerTemplate.xaml.cs
blob: 624eef296323a62ca1387138a27115d66827b9a5 (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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Tango.MachineStudio.Technician.PropertiesTemplates
{
    /// <summary>
    /// Interaction logic for MonitorTemplate.xaml
    /// </summary>
    public partial class ControllerTemplate : UserControl
    {
        public ControllerTemplate()
        {
            InitializeComponent();
        }
    }
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.BL;
using Tango.BL.Builders;
using Tango.BL.Entities;
using Tango.BL.Enumerations;
using Tango.Core.Commands;
using Tango.MachineStudio.Common;
using Tango.MachineStudio.Common.Notifications;
using Tango.MachineStudio.Statistics.Models;
using Tango.SharedUI;
using Tango.SharedUI.Components;
using Tango.AutoComplete.Editors;
using System.Windows.Media;
using LiveCharts.Wpf;
using LiveCharts;
using Tango.BL.ValueObjects;
using System.Diagnostics;
using Microsoft.Win32;
using Tango.CSV;
using System.ComponentModel;

namespace Tango.MachineStudio.Statistics.ViewModels
{
    public enum HeadCleaningSelectionEnum
    {
        [Description("Exclude")]
        Exclude = 0,
        [Description("Include")]
        Include = 1,
        [Description("Only")]
        Only = 2
    };


    public class JobRunsViewVM : ViewModel
    {
        private INotificationProvider _notification;
        private List<Machine> _allMachines;
        private List<User> _allUsers;
        private List<RmlModel> _rmlsModels;

        

        #region Properties

        private ObservableCollection<JobRunModel> _jobRuns;
        /// <summary>
        /// Gets or sets the job runs. Contains filtered data of JobRunModel.
        /// </summary>
        public ObservableCollection<JobRunModel> JobRuns
        {
            get { return _jobRuns; }
            set
            {
                _jobRuns = value;
                RaisePropertyChangedAuto();
            }
        }

        private JobRunModel _selectedJobRun = null;
        /// <summary>
        /// Gets or sets the JobRunModel. Binding to selected item of grid items.
        /// </summary>
        public JobRunModel SelectedJobRun
        {
            get { return _selectedJobRun; }
            set
            {
                _selectedJobRun = value;
                RaisePropertyChangedAuto();
            }
        }

        private SelectedObjectCollection<Machine> _selectedMachines;
        /// <summary>
        /// Gets or sets the selected machines. Contains all available machines and selected machines. Binding to ComboBox Machines.
        /// </summary>
        public SelectedObjectCollection<Machine> SelectedMachines
        {
            get { return _selectedMachines; }
            set
            {
                _selectedMachines = value;
                RaisePropertyChangedAuto();
            }
        }

        private DateTime _startSelectedDate;
        /// <summary>
        /// Gets or sets the start selected date.
        /// </summary>
        public DateTime StartSelectedDate
        {
            get { return _startSelectedDate; }
            set { _startSelectedDate = value;
                RaisePropertyChangedAuto();
            }
        }

        private DateTime _endSelectedDate;
        /// <summary>
        /// Gets or sets the end selected date.
        /// </summary>
        public DateTime EndSelectedDate
        {
            get { return _endSelectedDate; }
            set { _endSelectedDate = value; RaisePropertyChangedAuto(); }
        }

        protected Double _lengthLowerValue;
        /// <summary>
        /// Gets or sets the length lower value of Range Slider
        /// </summary>
        public Double LengthLowerValue
        {
            get { return _lengthLowerValue; }
            set
            {
                _lengthLowerValue = value;
                RaisePropertyChangedAuto();
            }
        }

        protected Double _lengthUpperValue;
        /// <summary>
        /// Gets or sets the length upper value of Range Slider.
        /// </summary>
        public Double LengthUpperValue
        {
            get { return _lengthUpperValue; }
            set
            {
                _lengthUpperValue = value;
                RaisePropertyChangedAuto();
            }
        }

        private SelectedObjectCollection<JobSource> _jobRunSelectedSources;
        /// <summary>
        /// Gets or sets the job run selected sources. Binding to ComboBox "Source".
        /// </summary>
        public SelectedObjectCollection<JobSource> JobRunSelectedSources
        {
            get { return _jobRunSelectedSources; }
            set { _jobRunSelectedSources = value; RaisePropertyChangedAuto(); }
        }

        private SelectedObjectCollection<JobRunStatus> _jobRunSelectedStatuses;
        /// <summary>
        /// Gets or sets the job run selected statuses. Binding to ComboBox "Status".
        /// </summary>
        public SelectedObjectCollection<JobRunStatus> JobRunSelectedStatuses
        {
            get { return _jobRunSelectedStatuses; }
            set { _jobRunSelectedStatuses = value; RaisePropertyChangedAuto(); }
        }

        public SelectedObjectCollection<bool> _isGradientSelection;
        /// <summary>
        /// Gets or sets the is gradient selection. Binding to ComboBox "IsGradient".
        /// </summary>
        public SelectedObjectCollection<bool> IsGradientSelection
        {
            get { return _isGradientSelection; }
            set
            {
                _isGradientSelection = value;
                RaisePropertyChangedAuto();
            }
        }

        private SelectedObjectCollection<RmlModel> _selectedThreads;
        /// <summary>
        /// Gets or sets the selected threads. Contains all available threads and selected threads. Binding to ComboBox "Thread".
        /// </summary>
        public SelectedObjectCollection<RmlModel> SelectedThreads
        {
            get { return _selectedThreads; }
            set
            {
                _selectedThreads = value;
                RaisePropertyChangedAuto();
            }
        }

        private HeadCleaningSelectionEnum _headCleaningSelected;

        public HeadCleaningSelectionEnum HeadCleaningSelected
        {
            get { return _headCleaningSelected; }
            set
            {
                _headCleaningSelected = value;
                RaisePropertyChangedAuto();
            }
        }


        /// <summary>
        /// Gets or sets the JobRuns providers.
        /// </summary>
        public ISuggestionProvider JobsProvider { get; set; }

        private Job _selectedJob;
        /// <summary>
        /// Gets or sets the job.
        /// </summary>
        public Job SelectedJob
        {
            get { return _selectedJob; }
            set
            {
                _selectedJob = value;
                RaisePropertyChangedAuto();
            }
        }

        /// <summary>
        /// Gets or sets the statistics value collection. Class - container included calculated statistic values.
        /// </summary>
        public StatisticsValueCollection StatisticsValueCollection { get; set; }

        #endregion

        public RelayCommand LoadJobRunsCommand { get; set; }

        public RelayCommand ExportToExcelCommand { get; set; }

        public JobRunsViewVM(INotificationProvider notificationProvider)
        {
            _notification = notificationProvider;
            JobRuns = new ObservableCollection<JobRunModel>();
            LoadJobRunsCommand = new RelayCommand(async () => await LoadJobRuns(), () => IsFree);
            ExportToExcelCommand = new RelayCommand(ExportToExcel, () => IsFree);
            LengthUpperValue = 10000.0;
            LengthLowerValue = 0.0;
            DateTime now = DateTime.Now;
            StartSelectedDate = now.AddMonths(-1);
            EndSelectedDate = now;

            JobRunSelectedSources = new SelectedObjectCollection<JobSource>(new ObservableCollection<JobSource>()
            {
                JobSource.Local,
                JobSource.Remote
            }, new ObservableCollection<JobSource>()
            {
                JobSource.Local,
                JobSource.Remote
            });
            JobRunSelectedSources.SelectionChanged -= (x, y) => RaisePropertyChanged(nameof(JobRunSelectedSources));
            JobRunSelectedSources.SelectionChanged += (x, y) => RaisePropertyChanged(nameof(JobRunSelectedSources));

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

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

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

            IsGradientSelection = new SelectedObjectCollection<bool>(new ObservableCollection<bool>
            {
                true,
                false
            }, new ObservableCollection<bool>
            {
                true,
                false
            });
            IsGradientSelection.SelectionChanged -= (x, y) => RaisePropertyChanged(nameof(IsGradientSelection));
            IsGradientSelection.SelectionChanged += (x, y) => RaisePropertyChanged(nameof(IsGradientSelection));

            HeadCleaningSelected = HeadCleaningSelectionEnum.Exclude;
            
            JobsProvider = new SuggestionProvider((filter) =>
            {
                try
                {
                    if (filter != null)
                    {
                        using (ObservablesContext db = ObservablesContext.CreateDefault())
                        {
                            return db.Jobs.Where(x => x.Name != null && x.Name.ToLower().Contains(filter.ToLower())).ToList();
                        }
                    }
                    else
                    {
                        return new List<Job>();
                    }
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error loading jobs.");
                    return null;
                }
            });

            StatisticsValueCollection = new StatisticsValueCollection();
        }

        /// <summary>
        /// Initializes this instance. Called form main view VM in OnApplicationReady
        /// </summary>
        public async void Init()
        {
            using (_notification.PushTaskItem("Loading job runs..."))
            {
                try
                {
                    IsFree = false;

                    using (var db = ObservablesContext.CreateDefault())
                    {
                        _allMachines = await db.Machines.ToListAsync();
                        _allUsers = await db.Users.Include(x => x.Contact).ToListAsync();
                        _rmlsModels = await db.Rmls.Select(x => new RmlModel() { Name = x.Name, Guid = x.Guid }).ToListAsync();
                        SelectedMachines = new SelectedObjectCollection<Machine>(_allMachines.ToObservableCollection(), new ObservableCollection<Machine>());
                        SelectedThreads = new SelectedObjectCollection<RmlModel>(_rmlsModels.ToObservableCollection(), new ObservableCollection<RmlModel>());
                    }
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error loading job runs.");
                }
                finally
                {
                    IsFree = true;
                }
            }
            
        }
        
        /// <summary>
        /// Loads the job runs by filters. 
        /// </summary>
        private async Task LoadJobRuns()
        {
            using (_notification.PushTaskItem("Loading job runs..."))
            {
                try
                {
                    IsFree = false;

                    using (var db = ObservablesContext.CreateDefault())
                    {
                        DateTime startUtc = new DateTime(StartSelectedDate.Year, StartSelectedDate.Month, StartSelectedDate.Day, 0, 0, 0).ToUniversalTime();
                        TimeSpan offsetTime = (EndSelectedDate.Date == DateTime.Now.Date) ? DateTime.Now.TimeOfDay : new TimeSpan(23, 59, 59);
                        DateTime endUtc = EndSelectedDate.ToUniversalTime() + offsetTime;

                        string jobName = SelectedJob == null ? "" : SelectedJob.Name;
                        
                        var db_JobRuns = db.JobRuns.Where(x => (x.StartDate <= endUtc && x.StartDate >= startUtc))
                            .Select(x => new
                            {
                                x.ID,
                                x.ActualStartDate,
                                x.EndDate,
                                x.EndPosition,
                                x.GradientResolutionCm,
                                x.Guid,
                                x.HeatingStartDate,
                                x.IsGradient,
                                x.JobGuid,
                                x.JobLength,
                                x.JobName,
                                x.JobSource,
                                x.MachineGuid,
                                x.RmlGuid,
                                x.StartDate,
                                x.Status,
                                x.UploadingStartDate,
                                x.UserGuid,
                                x.CyanQuantity,
                                x.MagentaQuantity,
                                x.YellowQuantity,
                                x.BlackQuantity,
                                x.TransparentQuantity,
                                x.LubricantQuantity,
                                x.CleanerQuantity,
                                x.IsHeadCleaning
                            });
                        var machineIDs = new HashSet<string>(SelectedMachines.SynchedSource.ToList().Select(p => p.Guid));
                        if (machineIDs.Count > 0)
                        {
                            db_JobRuns = db_JobRuns.Where(x => machineIDs.Contains(x.MachineGuid));
                        }
                        int[] jobRunSourceArr = JobRunSelectedSources.SynchedSource.Select(x => (int)x).ToArray();
                        if (jobRunSourceArr.Length > 0)
                        {
                            db_JobRuns = db_JobRuns.Where(x => jobRunSourceArr.Contains(x.JobSource));
                        }
                        int[] jobRunStatusArr = JobRunSelectedStatuses.SynchedSource.Select(x => (int)x).ToArray();
                        if (jobRunStatusArr.Length > 0)
                        {
                            db_JobRuns = db_JobRuns.Where(x => jobRunStatusArr.Contains(x.Status));
                        }
                        bool[] isGradientArr = IsGradientSelection.SynchedSource.Select(x => (bool)x).ToArray();
                        if (isGradientArr.Length > 0)
                        {
                            db_JobRuns = db_JobRuns.Where(x => isGradientArr.Contains(x.IsGradient));
                        }
                        
                        if(HeadCleaningSelected != HeadCleaningSelectionEnum.Include)
                        {
                            bool isHeadCleaning = HeadCleaningSelected == HeadCleaningSelectionEnum.Only;
                            db_JobRuns = db_JobRuns.Where(x => isHeadCleaning == x.IsHeadCleaning);
                        }

                        List<String> rmlGuids = SelectedThreads.SynchedSource.Select(y => y.Guid).ToList();
                        if (rmlGuids != null && rmlGuids.Count > 0)
                        {
                            db_JobRuns = db_JobRuns.Where(x => rmlGuids.Contains(x.RmlGuid));
                        }
                        if (!String.IsNullOrEmpty(jobName))
                        {
                            db_JobRuns = db_JobRuns.Where(x => x.JobName.ToLower().StartsWith(jobName.ToLower()));
                        }

                        var runs_db = await db_JobRuns.ToListAsync(); //Execute actual query.


                        List<JobRun> runs = runs_db.Where(x => (x.JobLength < LengthUpperValue && x.JobLength >= LengthLowerValue))
                            .Select(x => new JobRun()
                            {
                                ID = x.ID,
                                ActualStartDate = x.ActualStartDate,
                                EndDate = x.EndDate,
                                EndPosition = x.EndPosition,
                                GradientResolutionCm = x.GradientResolutionCm,
                                Guid = x.Guid,
                                HeatingStartDate = x.HeatingStartDate,
                                IsGradient = x.IsGradient,
                                JobGuid = x.JobGuid,
                                JobLength = x.JobLength,
                                JobName = x.JobName,
                                JobSource = x.JobSource,
                                MachineGuid = x.MachineGuid,
                                RmlGuid = x.RmlGuid,
                                StartDate = x.StartDate,
                                Status = x.Status,
                                UploadingStartDate = x.UploadingStartDate,
                                UserGuid = x.UserGuid,
                                CyanQuantity = x.CyanQuantity,
                                MagentaQuantity = x.MagentaQuantity,
                                YellowQuantity = x.YellowQuantity,
                                BlackQuantity = x.BlackQuantity,
                                TransparentQuantity = x.TransparentQuantity,
                                LubricantQuantity = x.LubricantQuantity,
                                CleanerQuantity = x.CleanerQuantity,
                                IsHeadCleaning = x.IsHeadCleaning
                            }).ToList();
                        
                        var modelList = runs.Select(x => new JobRunModel()
                        {
                            JobRun = x,
                            Machine = _allMachines.FirstOrDefault(y => y.Guid == x.MachineGuid),
                            User = _allUsers.SingleOrDefault(y => y.Guid == x.UserGuid),
                            Rml = _rmlsModels.SingleOrDefault(y => y.Guid == x.RmlGuid),
                        }).OrderByDescending(x => x.JobRun.StartDate).ToList();

                        modelList.ForEach(x => x.Init());
                        JobRuns = modelList.ToObservableCollection();
                        GenerateStatistics();
                    }


                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error loading job runs.");
                }
                finally
                {
                    IsFree = true;
                }
            }
        }

        private void ExportToExcel()
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Title = "Job Runs Statistic Report";
            dlg.Filter = "CSV Files|*.csv";
            dlg.FileName = $"Statistics_Job_runs";
            dlg.DefaultExt = ".csv";
            if (dlg.ShowDialog().Value)
            {
                try
                {
                    CsvFile<ExcelModel> csvFile = new CsvFile<ExcelModel>(new CsvDestination(dlg.FileName), new CsvDefinition()
                    {
                        Columns = new List<String>()
                         {
                             "ID",
                             "Machine",
                             "User",
                             "Job Name",
                             "Thread",
                             "Length",
                             "Source",
                             "Upload Duration",
                             "Heating Duration",
                             "Start Time",
                             "IsGradient",
                             "Gradient Resolution",
                             "Status",
                             "End Date",
                             "End Position",
                             "Cyan",
                             "Magenta",
                             "Yellow",
                             "Black",
                             "Transparent",
                             "Lubricant",
                             "Cleaner"
                         },
                    });
                    var selection = JobRuns.Where(z => z.JobRun.EndPosition > 0 && z.JobRun.EndDate != null && z.JobRun.ActualStartDate != null);
                    foreach (var jobRunModel in selection)
                    {
                        ExcelModel excel_model = new ExcelModel();
                        excel_model.ID = jobRunModel.JobRun.ID.ToString();
                        excel_model.Machine = jobRunModel.Machine != null ? jobRunModel.Machine.SerialNumber : "";
                        excel_model.User = jobRunModel.User != null ? jobRunModel.User.Contact.FullName: "";
                        excel_model.JobName = jobRunModel.JobRun.JobName;
                        excel_model.Thread = jobRunModel.Rml != null ? jobRunModel.Rml.Name : "";
                        excel_model.Length = String.Format("{0:0.##}", jobRunModel.JobRun.JobLength);
                        excel_model.Source = jobRunModel.JobRun.Source.ToString();
                        excel_model.UploadDuration = jobRunModel.UploadDuration != null ? ((TimeSpan)(jobRunModel.UploadDuration)).ToString(@"hh\:mm\:ss") : TimeSpan.FromSeconds(0).ToString(@"hh\:mm\:ss");
                        excel_model.HeatingDuration = jobRunModel.HeatingDuration != null ? ((TimeSpan)(jobRunModel.HeatingDuration)).ToString(@"hh\:mm\:ss") : TimeSpan.FromSeconds(0).ToString(@"hh\:mm\:ss");
                        excel_model.StartTime = jobRunModel.JobRun.ActualStartDate != null ? ((DateTime)jobRunModel.JobRun.ActualStartDate).ToLocalTime().ToString("MM/dd/yy HH:mm"): "";
                        excel_model.IsGradient = jobRunModel.JobRun.IsGradient ? "Yes" : "No";
                        excel_model.GR = jobRunModel.JobRun.GradientResolutionCm.ToString();
                        excel_model.Status = jobRunModel.JobRun.JobRunStatus.ToString();
                        excel_model.EndTime = jobRunModel.JobRun.EndDate != null ? ((DateTime)jobRunModel.JobRun.EndDate).ToLocalTime().ToString("MM/dd/yy HH:mm"): "";
                        excel_model.EndPosition = String.Format("{0:0.##}", jobRunModel.JobRun.EndPosition);
                        excel_model.Cyan = jobRunModel.JobRun.CyanQuantity < 0 ? "" :jobRunModel.JobRun.CyanQuantity.ToString();
                        excel_model.Magenta = jobRunModel.JobRun.MagentaQuantity < 0 ? "" : jobRunModel.JobRun.MagentaQuantity.ToString();
                        excel_model.Yellow = jobRunModel.JobRun.YellowQuantity < 0 ? "" : jobRunModel.JobRun.YellowQuantity.ToString();
                        excel_model.Black = jobRunModel.JobRun.BlackQuantity < 0 ? "" : jobRunModel.JobRun.BlackQuantity.ToString();
                        excel_model.Transparent = jobRunModel.JobRun.TransparentQuantity < 0 ? "" : jobRunModel.JobRun.TransparentQuantity.ToString();
                        excel_model.Lubricant = jobRunModel.JobRun.LubricantQuantity < 0 ? "" : jobRunModel.JobRun.LubricantQuantity.ToString();
                        excel_model.Cleaner = jobRunModel.JobRun.CleanerQuantity < 0 ? "" : jobRunModel.JobRun.CleanerQuantity.ToString();
                        csvFile.Append(excel_model);
                        
                    }

                    csvFile.Dispose();
                    _notification.ShowInfo("Report generated successfully.");
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex, "Error generating Statistics Job Runs report.");
                    _notification.ShowError($"Error generating Statistics Job Runs report..\n{ex.Message}");
                }
            }
        }
            #region GenerateS_StatisticsValueCollection 

            /// <summary>
            /// Generates the statistics.
            /// </summary>
            protected void GenerateStatistics()
        {
            StatisticsValueCollection.Clean();
            if (JobRuns.Count() == 0)
                return;
            GenerateTotalRunsCount();
            GenerateTotalRunsLength();
            GenerateTotalThreadConsumption();
            GenerateRunsDuration();
            GenerateAverageUploadDuration();
            GenerateAverageHeatingDuration();

            GeneratePieCharts();
            CreateThreadConsumptionPerThread();
            GenerateAllLiquidQuantities();
        }

        protected void GenerateTotalRunsCount()
        {//Total Runs: 
            int val = JobRuns.Count();
            StatisticsValueCollection.AddStatisticsValue("Total Runs ", val, " ");
        }

        /// <summary>
        /// Generates the total length of the job runs.
        /// </summary>
        protected void GenerateTotalRunsLength()
        {
            double val = JobRuns.Where(z => z.JobRun.EndPosition > 0).Sum(x => x.JobRun.JobLength);
            StatisticsValueCollection.AddStatisticsValue("Total Runs Length", val, " m");
        }

        /// <summary>
        /// Generates the duration and average of the job runs.
        /// </summary>
        protected void GenerateRunsDuration()
        {
            var selection = JobRuns.Where(z => z.JobRun.EndPosition > 0 && z.JobRun.EndDate != null && z.JobRun.ActualStartDate != null);
            double val = 0d;
            double average = 0d;
            if (selection != null && selection.Count<JobRunModel>() > 0)
            {
                val = selection.Sum(x => (x.JobRun.EndDate - x.JobRun.ActualStartDate).Value.TotalHours);
                average = selection.Average(x => (x.JobRun.EndDate - x.JobRun.ActualStartDate).Value.TotalMilliseconds);
            }
            StatisticsValueCollection.AddStatisticsValue("Total Dyeing Time", val, " hours");
            StatisticsValueCollection.AddStatisticsValue("Average Dyeing Time", Math.Max(TimeSpan.FromMilliseconds(average).TotalHours, 0), " hours");
        }

        /// <summary>
        /// Generates the average upload duration of the job runs.
        /// </summary>
        protected void GenerateAverageUploadDuration()
        {
            var average = (long)JobRuns.Where(z => z.JobRun.EndPosition > 0 && z.UploadDuration != null).Average(x => x.UploadDuration.Value.TotalMilliseconds);
            StatisticsValueCollection.AddStatisticsValue("Average Upload Duration", Math.Max(TimeSpan.FromMilliseconds(average).TotalMinutes, 0), " minutes");
        }

        /// <summary>
        /// Generates the average duration heating of the job runs.
        /// </summary>
        protected void GenerateAverageHeatingDuration()
        {
            var average = JobRuns.Where(z => z.JobRun.EndPosition > 0 && z.HeatingDuration != null && z.HeatingDuration.Value.Ticks > 0).Average(x => x.HeatingDuration.Value.TotalMilliseconds);
            StatisticsValueCollection.AddStatisticsValue("Average Heating Duration", Math.Max(TimeSpan.FromMilliseconds(average).TotalMinutes, 0), " minutes");
        }

        /// <summary>
        /// Generates the total thread consumption by EndPosition.
        /// </summary>
        protected void GenerateTotalThreadConsumption()
        {
            double val = JobRuns.Where(z => z.JobRun.EndPosition > 0).Sum(x => x.JobRun.EndPosition);
            StatisticsValueCollection.AddStatisticsValue("Total Dyeing Length", val, " m");
        }

        /// <summary>
        /// Generates the pie charts in percentage: JobSource, JobRunStatus, Gradient.
        /// </summary>
        protected void GeneratePieCharts()
        {
            int PPCCount = JobRuns.Count(x => x.JobRun.Source == JobSource.Local);
            int MSCount = JobRuns.Count(x => x.JobRun.Source == JobSource.Remote);
            StatisticsValueCollection.GeneratePieJobSource(PPCCount, MSCount);

            int failedCount = JobRuns.Count(x => x.JobRun.JobRunStatus == JobRunStatus.Failed);
            int abortedCount = JobRuns.Count(x => x.JobRun.JobRunStatus == JobRunStatus.Aborted);
            int completedCount = JobRuns.Count(x => x.JobRun.JobRunStatus == JobRunStatus.Completed);
            StatisticsValueCollection.GeneratePieJobRunStatus(failedCount, abortedCount, completedCount);

            int gradientCount = JobRuns.Count(x => x.JobRun.IsGradient == true);
            int solidCount = JobRuns.Count(x => x.JobRun.IsGradient == false);
            StatisticsValueCollection.GeneratePieGradientSolid(gradientCount, solidCount);

        }

        /// <summary>
        /// Creates the thread consumption per thread.
        /// </summary>
        protected void CreateThreadConsumptionPerThread()
        {
            var temp = JobRuns.Where(z => z.JobRun.EndPosition > 0 && z.Rml != null).GroupBy(x => x.Rml.Name);
            List<StatisticsValue> result = JobRuns.Where(z => z.JobRun.EndPosition > 0 && z.Rml != null && !String.IsNullOrEmpty(z.Rml.Name)).GroupBy(x => x.Rml.Name).Select(y => new StatisticsValue { Name = y.Key, Value = y.Sum(x => x.JobRun.EndPosition), Unit = "m" }).ToList();
            StatisticsValueCollection.CreateThreadConsumptionPerThread(result);
        }

        /// <summary>
        /// Generates all liquid quantities.
        /// </summary>
        protected void GenerateAllLiquidQuantities()
        {
            var runs = JobRuns.Where(z => z.JobRun.EndPosition > 0 && z.JobRun.LiquidQuantitiesFast.Count > 0).ToList();

            Dictionary<LiquidTypes, ulong> total_quantities = new Dictionary<LiquidTypes, ulong>();

            foreach (LiquidTypes ltype in (LiquidTypes[])Enum.GetValues(typeof(LiquidTypes)))
            {
                total_quantities[ltype] = 0;
            }

            foreach (var run in runs)
            {
                foreach (var lq in run.JobRun.LiquidQuantitiesFast)
                {
                    if (lq.Quantity < 0)
                    {
                        Debug.WriteLine($"Warning: JobRun '{run.JobRun.ID}' contains an invalid value '{lq.Quantity}' for {lq.LiquidType} quantity.");
                    }

                    total_quantities[lq.LiquidType] += Convert.ToUInt64(Math.Max(lq.Quantity, 0));
                }
            }

            List<TotalLiquidQuantityModel> allLiquidQuantities = total_quantities.Select(x => new TotalLiquidQuantityModel()
            {
                LiquidType = x.Key,
                Quantity = x.Value
            }).ToList();


            //foreach (LiquidTypes ltype in (LiquidTypes[])Enum.GetValues(typeof(LiquidTypes)))
            //{
            //    var liquidQuantityByTypeList = db_liquidQuantities.Select(x => x.FirstOrDefault(y => y.LiquidType == ltype)).Where(x => x != null);
            //    var count = liquidQuantityByTypeList != null ? liquidQuantityByTypeList.Sum(x => x.Quantity) : 0;
            //    JobRunLiquidQuantity lq = new JobRunLiquidQuantity() { LiquidType = ltype, Quantity = count };
            //    allLiquidQuantities.Add(lq);
            //}
            StatisticsValueCollection.GenerateStatisticsLiquidQuantity(allLiquidQuantities);
        }
        #endregion

    }
}