Last-Modified: Wed, 15 Jul 2026 04:42:19 GMT
Expires: Sat, 12 Jul 2036 04:42:19 GMT
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;
namespace Tango.MachineStudio.Statistics.ViewModels
{
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();
}
}
/// <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 JobRunsViewVM(INotificationProvider notificationProvider)
{
_notification = notificationProvider;
JobRuns = new ObservableCollection<JobRunModel>();
LoadJobRunsCommand = new RelayCommand(async () => await LoadJobRuns(), () => IsFree);
LengthUpperValue = 5000.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));
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
});
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));
}
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
}).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;
}
}
}
#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("Total 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
}
}