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 _allMachines; private List _allUsers; private List _rmlsModels; #region Properties private ObservableCollection _jobRuns; /// /// Gets or sets the job runs. Contains filtered data of JobRunModel. /// public ObservableCollection JobRuns { get { return _jobRuns; } set { _jobRuns = value; RaisePropertyChangedAuto(); } } private JobRunModel _selectedJobRun = null; /// /// Gets or sets the JobRunModel. Binding to selected item of grid items. /// public JobRunModel SelectedJobRun { get { return _selectedJobRun; } set { _selectedJobRun = value; RaisePropertyChangedAuto(); } } private SelectedObjectCollection _selectedMachines; /// /// Gets or sets the selected machines. Contains all available machines and selected machines. Binding to ComboBox Machines. /// public SelectedObjectCollection SelectedMachines { get { return _selectedMachines; } set { _selectedMachines = value; RaisePropertyChangedAuto(); } } private DateTime _startSelectedDate; /// /// Gets or sets the start selected date. /// public DateTime StartSelectedDate { get { return _startSelectedDate; } set { _startSelectedDate = value; RaisePropertyChangedAuto(); } } private DateTime _endSelectedDate; /// /// Gets or sets the end selected date. /// public DateTime EndSelectedDate { get { return _endSelectedDate; } set { _endSelectedDate = value; RaisePropertyChangedAuto(); } } protected Double _lengthLowerValue; /// /// Gets or sets the length lower value of Range Slider /// public Double LengthLowerValue { get { return _lengthLowerValue; } set { _lengthLowerValue = value; RaisePropertyChangedAuto(); } } protected Double _lengthUpperValue; /// /// Gets or sets the length upper value of Range Slider. /// public Double LengthUpperValue { get { return _lengthUpperValue; } set { _lengthUpperValue = value; RaisePropertyChangedAuto(); } } private SelectedObjectCollection _jobRunSelectedSources; /// /// Gets or sets the job run selected sources. Binding to ComboBox "Source". /// public SelectedObjectCollection JobRunSelectedSources { get { return _jobRunSelectedSources; } set { _jobRunSelectedSources = value; RaisePropertyChangedAuto(); } } private SelectedObjectCollection _jobRunSelectedStatuses; /// /// Gets or sets the job run selected statuses. Binding to ComboBox "Status". /// public SelectedObjectCollection JobRunSelectedStatuses { get { return _jobRunSelectedStatuses; } set { _jobRunSelectedStatuses = value; RaisePropertyChangedAuto(); } } public SelectedObjectCollection _isGradientSelection; /// /// Gets or sets the is gradient selection. Binding to ComboBox "IsGradient". /// public SelectedObjectCollection IsGradientSelection { get { return _isGradientSelection; } set { _isGradientSelection = value; RaisePropertyChangedAuto(); } } private SelectedObjectCollection _selectedThreads; /// /// Gets or sets the selected threads. Contains all available threads and selected threads. Binding to ComboBox "Thread". /// public SelectedObjectCollection SelectedThreads { get { return _selectedThreads; } set { _selectedThreads = value; RaisePropertyChangedAuto(); } } private HeadCleaningSelectionEnum _headCleaningSelected; public HeadCleaningSelectionEnum HeadCleaningSelected { get { return _headCleaningSelected; } set { _headCleaningSelected = value; RaisePropertyChangedAuto(); } } /// /// Gets or sets the JobRuns providers. /// public ISuggestionProvider JobsProvider { get; set; } private Job _selectedJob; /// /// Gets or sets the job. /// public Job SelectedJob { get { return _selectedJob; } set { _selectedJob = value; RaisePropertyChangedAuto(); } } /// /// Gets or sets the statistics value collection. Class - container included calculated statistic values. /// 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(); 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(new ObservableCollection() { JobSource.Local, JobSource.Remote }, new ObservableCollection() { JobSource.Local, JobSource.Remote }); JobRunSelectedSources.SelectionChanged -= (x, y) => RaisePropertyChanged(nameof(JobRunSelectedSources)); JobRunSelectedSources.SelectionChanged += (x, y) => RaisePropertyChanged(nameof(JobRunSelectedSources)); JobRunSelectedStatuses = new SelectedObjectCollection(new ObservableCollection() { JobRunStatus.Aborted, JobRunStatus.Completed, JobRunStatus.Failed, }, new ObservableCollection() { JobRunStatus.Aborted, JobRunStatus.Completed, JobRunStatus.Failed, }); JobRunSelectedStatuses.SelectionChanged -= (x, y) => RaisePropertyChanged(nameof(JobRunSelectedStatuses)); JobRunSelectedStatuses.SelectionChanged += (x, y) => RaisePropertyChanged(nameof(JobRunSelectedStatuses)); IsGradientSelection = new SelectedObjectCollection(new ObservableCollection { true, false }, new ObservableCollection { 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(); } } catch (Exception ex) { LogManager.Log(ex, "Error loading jobs."); return null; } }); StatisticsValueCollection = new StatisticsValueCollection(); } /// /// Initializes this instance. Called form main view VM in OnApplicationReady /// 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(_allMachines.ToObservableCollection(), new ObservableCollection()); SelectedThreads = new SelectedObjectCollection(_rmlsModels.ToObservableCollection(), new ObservableCollection()); } } catch (Exception ex) { LogManager.Log(ex, "Error loading job runs."); } finally { IsFree = true; } } } /// /// Loads the job runs by filters. /// 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(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 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 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 csvFile = new CsvFile(new CsvDestination(dlg.FileName), new CsvDefinition() { Columns = new List() { "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 /// /// Generates the statistics. /// 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, " "); } /// /// Generates the total length of the job runs. /// protected void GenerateTotalRunsLength() { double val = JobRuns.Where(z => z.JobRun.EndPosition > 0).Sum(x => x.JobRun.JobLength); StatisticsValueCollection.AddStatisticsValue("Total Runs Length", val, " m"); } /// /// Generates the duration and average of the job runs. /// 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() > 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"); } /// /// Generates the average upload duration of the job runs. /// 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"); } /// /// Generates the average duration heating of the job runs. /// 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"); } /// /// Generates the total thread consumption by EndPosition. /// protected void GenerateTotalThreadConsumption() { double val = JobRuns.Where(z => z.JobRun.EndPosition > 0).Sum(x => x.JobRun.EndPosition); StatisticsValueCollection.AddStatisticsValue("Total Dyeing Length", val, " m"); } /// /// Generates the pie charts in percentage: JobSource, JobRunStatus, Gradient. /// 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); } /// /// Creates the thread consumption per thread. /// protected void CreateThreadConsumptionPerThread() { var temp = JobRuns.Where(z => z.JobRun.EndPosition > 0 && z.Rml != null).GroupBy(x => x.Rml.Name); List 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); } /// /// Generates all liquid quantities. /// protected void GenerateAllLiquidQuantities() { var runs = JobRuns.Where(z => z.JobRun.EndPosition > 0 && z.JobRun.LiquidQuantitiesFast.Count > 0).ToList(); Dictionary total_quantities = new Dictionary(); 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 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 } }