diff options
Diffstat (limited to 'Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics')
14 files changed, 1184 insertions, 450 deletions
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/DateIsInListToBooleanConverter.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/DateIsInListToBooleanConverter.cs new file mode 100644 index 000000000..74e0c61d8 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/DateIsInListToBooleanConverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; + +namespace Tango.MachineStudio.Statistics.Converters +{ + public class DateIsInListToBooleanConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values.Length < 2 || !(values[0] is DateTime) || !(values[1] is IEnumerable<DateTime>)) + return false; + + var date = (DateTime)values[0]; + var dateList = (IEnumerable<DateTime>)values[1]; + + return dateList.ToList().Exists(x => x.ToLocalTime().ToShortDateString() == date.ToLocalTime().ToShortDateString()); + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/StringToBoolYesNoNullConverter.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/StringToBoolYesNoNullConverter.cs new file mode 100644 index 000000000..f7633a7d0 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Converters/StringToBoolYesNoNullConverter.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls; +using System.Windows.Data; + +namespace Tango.MachineStudio.Statistics.Converters +{ + public class StringToBoolYesNoNullConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if(value is bool) + { + return (bool)value ? "Yes" : "No"; + } + return "Null"; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + //throw new NotImplementedException(); + if (value is ComboBoxItem) + { + string str_val = ((ComboBoxItem)value).Content.ToString(); + if (str_val.ToUpper() == "NO") + return false; + if (str_val.ToUpper() == "YES") + return true; + } + return null; + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/JobRunModel.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/JobRunModel.cs index 8e5642e3d..e269f761f 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/JobRunModel.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/JobRunModel.cs @@ -1,45 +1,37 @@ - -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using Tango.BL; using Tango.BL.Entities; -using Tango.Core.ExtensionMethods; namespace Tango.MachineStudio.Statistics.Models { - public class JobRunModel : JobRun + public class JobRunModel { - private static Dictionary<String, Machine> _machines = new Dictionary<string, Machine>(); + public JobRun JobRun { get; set; } - public JobRunModel() - { + public Machine Machine { get; set; } - } + public User User { get; set; } - public JobRunModel(JobRun run) - { - run.MapPropertiesTo(this, MappingFlags.NoReferenceTypes); - } + public TimeSpan? UploadDuration { get; set; } + + public TimeSpan? HeatingDuration { get; set; } - public Task LoadMachine(ObservablesContext context) + + + public void Init() { - return Task.Factory.StartNew(() => + if (JobRun.HeatingStartDate != null) { - if (!_machines.ContainsKey(MachineGuid)) - { - Machine = context.Machines.SingleOrDefault(x => x.Guid == MachineGuid); - _machines.Add(MachineGuid, Machine); - } - else - { - Machine = _machines[MachineGuid]; - } - }); - } + UploadDuration = JobRun.HeatingStartDate - JobRun.StartDate; + } - public Machine Machine { get; set; } + if (JobRun.ActualStartDate != null && JobRun.HeatingStartDate != null) + { + HeatingDuration = JobRun.ActualStartDate - JobRun.HeatingStartDate; + } + } } } diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/JobRunStatisticsModel.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/JobRunStatisticsModel.cs new file mode 100644 index 000000000..98b719cae --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Models/JobRunStatisticsModel.cs @@ -0,0 +1,45 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.BL; +using Tango.BL.Entities; +using Tango.Core.ExtensionMethods; + +namespace Tango.MachineStudio.Statistics.Models +{ + public class JobRunStatisticsModel : JobRun + { + private static Dictionary<String, Machine> _machines = new Dictionary<string, Machine>(); + + public JobRunStatisticsModel() + { + + } + + public JobRunStatisticsModel(JobRun run) + { + run.MapPropertiesTo(this, MappingFlags.NoReferenceTypes); + } + + public Task LoadMachine(ObservablesContext context) + { + return Task.Factory.StartNew(() => + { + if (!_machines.ContainsKey(MachineGuid)) + { + Machine = context.Machines.SingleOrDefault(x => x.Guid == MachineGuid); + _machines.Add(MachineGuid, Machine); + } + else + { + Machine = _machines[MachineGuid]; + } + }); + } + + public Machine Machine { get; set; } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Tango.MachineStudio.Statistics.csproj b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Tango.MachineStudio.Statistics.csproj index 36c371165..4b3535f83 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Tango.MachineStudio.Statistics.csproj +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Tango.MachineStudio.Statistics.csproj @@ -73,14 +73,25 @@ <Reference Include="PresentationFramework" /> </ItemGroup> <ItemGroup> + <Compile Include="Converters\StringToBoolYesNoNullConverter.cs" /> + <Compile Include="Converters\DateIsInListToBooleanConverter.cs" /> <Compile Include="Models\JobRunModel.cs" /> + <Compile Include="Models\JobRunStatisticsModel.cs" /> <Compile Include="Models\LabeledSeriesCollection.cs" /> <Compile Include="Tooltips\PieChartTooltipControl.xaml.cs"> <DependentUpon>PieChartTooltipControl.xaml</DependentUpon> </Compile> <Compile Include="StatisticsModule.cs" /> <Compile Include="ViewModelLocator.cs" /> + <Compile Include="ViewModels\ChartsViewVM.cs" /> + <Compile Include="ViewModels\JobRunsViewVM.cs" /> <Compile Include="ViewModels\MainViewVM.cs" /> + <Compile Include="Views\ChartsView.xaml.cs"> + <DependentUpon>ChartsView.xaml</DependentUpon> + </Compile> + <Compile Include="Views\JobRunsView.xaml.cs"> + <DependentUpon>JobRunsView.xaml</DependentUpon> + </Compile> <Compile Include="Views\MainView.xaml.cs"> <DependentUpon>MainView.xaml</DependentUpon> </Compile> @@ -95,6 +106,14 @@ <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> + <Page Include="Views\ChartsView.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Include="Views\JobRunsView.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> <Page Include="Views\MainView.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/ViewModels/ChartsViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/ViewModels/ChartsViewVM.cs new file mode 100644 index 000000000..b06261306 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/ViewModels/ChartsViewVM.cs @@ -0,0 +1,362 @@ +using LiveCharts; +using LiveCharts.Wpf; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Media; +using Tango.BL.Enumerations; +using Tango.MachineStudio.Common; +using Tango.MachineStudio.Statistics.Models; +using Tango.BL.Entities; +using Tango.SharedUI; +using Tango.BL; +using Tango.MachineStudio.Common.Notifications; +using System.Threading.Tasks; + +namespace Tango.MachineStudio.Statistics.ViewModels +{ + public class ChartsViewVM : ViewModel + { + private INotificationProvider _notification; + private ObservablesContext _context; + private List<JobRunStatisticsModel> _job_runs; + private bool _loaded; + + #region Properties + private LabeledSeriesCollection _timelineJobStatusSeries; + public LabeledSeriesCollection TimelineJobStatusSeries + { + get { return _timelineJobStatusSeries; } + set { _timelineJobStatusSeries = value; RaisePropertyChangedAuto(); } + } + + private LabeledSeriesCollection _pieJobFailedReasons; + public LabeledSeriesCollection PieJobFailedReasons + { + get { return _pieJobFailedReasons; } + set { _pieJobFailedReasons = value; RaisePropertyChangedAuto(); } + } + + private LabeledSeriesCollection _printPerWeekSeries; + public LabeledSeriesCollection PrintPerWeekSeries + { + get { return _printPerWeekSeries; } + set { _printPerWeekSeries = value; RaisePropertyChangedAuto(); } + } + + private DateTime _startDate; + public DateTime StartDate + { + get { return _startDate; } + set { _startDate = value; RaisePropertyChangedAuto(); OnDateRangeChanged(); } + } + + private DateTime _endDate; + public DateTime EndDate + { + get { return _endDate; } + set { _endDate = value; RaisePropertyChangedAuto(); OnDateRangeChanged(); } + } + + private DateTime _minDate; + public DateTime MinDate + { + get { return _minDate; } + set { _minDate = value; RaisePropertyChangedAuto(); } + } + + private DateTime _maxDate; + public DateTime MaxDate + { + get { return _maxDate; } + set { _maxDate = value; RaisePropertyChangedAuto(); } + } + #endregion + + public ChartsViewVM(INotificationProvider notificationProvider) + { + _notification = notificationProvider; + StartDate = DateTime.Now.AddMonths(-1); + EndDate = DateTime.Now; + } + + #region Generate Charts + + public async void Init() + { + using (_notification.PushTaskItem("Loading statistics...")) + { + IsFree = false; + + await Task.Factory.StartNew(() => + { + _context = ObservablesContext.CreateDefault(); + _job_runs = _context.JobRuns.OrderBy(x => x.StartDate).ToList().Select(x => new JobRunStatisticsModel(x)).ToList(); + foreach (var run in _job_runs) + { + run.LoadMachine(_context).GetAwaiter().GetResult(); + } + }); + + if (_job_runs.Count > 0) + { + MinDate = _job_runs.Min(x => x.StartDate); + MaxDate = _job_runs.Max(x => x.StartDate); + } + + InvokeUIOnIdle(() => + { + if (_loaded) + { + OnDateRangeChanged(); + } + }); + + _loaded = true; + IsFree = true; + } + } + + private List<JobRunStatisticsModel> GetJobRunsByDateRange(DateTime startDate, DateTime endTime, JobRunStatus? status = null) + { + return _job_runs.Where(x => x.StartDate.ToLocalTime() >= startDate && x.StartDate.ToLocalTime() <= endTime && (status == null || x.JobRunStatus == status)).ToList(); + } + + private List<JobRunStatisticsModel> GetJobRunsByDate(DateTime date, JobRunStatus? status = null) + { + return _job_runs.Where(x => x.StartDate.ToLocalTime().Date == date.Date && (status == null || x.JobRunStatus == status)).ToList(); + } + + private IEnumerable<DateTime> CreateDates(DateTime start, DateTime end) + { + for (DateTime date = start.Date; date.Date <= end.Date; date = date.AddDays(1)) + { + yield return date; + } + } + + private void GenerateTimelineJobStatusChart() + { + TimelineJobStatusSeries = new LabeledSeriesCollection() + { + Title = "Job Runs Status", + ChartTitle = "Number Of Runs", + LabelsTitle = "Date", + SeriesColors = new List<Color>() + { + Colors.Green, + Colors.Orange, + Colors.Red, + }, + }; + + Series completed_job_runs = new ColumnSeries() + { + Title = "Completed", + Values = new ChartValues<int>(), + Fill = Brushes.Green, + MinWidth = 1, + + }; + Series aborted_job_runs = new ColumnSeries() + { + Title = "Aborted", + Values = new ChartValues<int>(), + Fill = Brushes.Orange, + MinWidth = 1, + }; + Series failed_job_runs = new ColumnSeries() + { + Title = "Failed", + Values = new ChartValues<int>(), + Fill = Brushes.Red, + MinWidth = 1, + }; + + if (EndDate - StartDate > TimeSpan.FromDays(40)) + { + completed_job_runs = new LineSeries() + { + Title = "Completed", + Values = new ChartValues<int>(), + Fill = new SolidColorBrush(Colors.Green) { Opacity = 0.5 }, + MinWidth = 1, + PointGeometry = null, + StrokeThickness = 0, + + }; + aborted_job_runs = new LineSeries() + { + Title = "Aborted", + Values = new ChartValues<int>(), + Fill = new SolidColorBrush(Colors.Orange) { Opacity = 0.5 }, + MinWidth = 1, + PointGeometry = null, + StrokeThickness = 0, + }; + failed_job_runs = new LineSeries() + { + Title = "Failed", + Values = new ChartValues<int>(), + Fill = new SolidColorBrush(Colors.Red) { Opacity = 0.5 }, + MinWidth = 1, + PointGeometry = null, + StrokeThickness = 0, + }; + } + + foreach (var date in CreateDates(StartDate, EndDate)) + { + completed_job_runs.Values.Add(GetJobRunsByDate(date, JobRunStatus.Completed).Count()); + aborted_job_runs.Values.Add(GetJobRunsByDate(date, JobRunStatus.Aborted).Count()); + failed_job_runs.Values.Add(GetJobRunsByDate(date, JobRunStatus.Failed).Count()); + + TimelineJobStatusSeries.Labels.Add(date.ToShortDateString()); + } + + + + TimelineJobStatusSeries.SeriesCollection.Add(failed_job_runs); + TimelineJobStatusSeries.SeriesCollection.Add(aborted_job_runs); + TimelineJobStatusSeries.SeriesCollection.Add(completed_job_runs); + } + + private void GeneratePieFailedReasonsChart() + { + var groups = GetJobRunsByDateRange(StartDate, EndDate, JobRunStatus.Failed).GroupBy(x => x.FailedMessage).OrderBy(x => x.Count()); + + List<Color> colors = new List<Color>(); + + int max = groups.Count() > 0 ? groups.Max(x => x.Count()) : 0; + + for (int i = 0; i < groups.Count(); i++) + { + int count = groups.ElementAt(i).Count(); + double alpha = Math.Max(((double)(count) / max * 200), 20); + colors.Add(Color.FromArgb((byte)alpha, 200, 0, 0)); + } + + PieJobFailedReasons = new LabeledSeriesCollection() + { + Title = "Job Failure Reasons", + SeriesColors = colors, + }; + + int index = 0; + + foreach (var group in groups) + { + int count = group.Count(); + + var series = new PieSeries() + { + Title = group.First().FailedMessage, + Values = new ChartValues<int>() { count }, + Fill = new SolidColorBrush(colors[index++]), + DataLabels = true, + ToolTip = group.First().FailedMessage, + }; + + PieJobFailedReasons.SeriesCollection.Add(series); + } + } + + private void GeneratePrintPerWeekChart() + { + List<JobRunStatisticsModel> range_job_runs = GetJobRunsByDateRange(StartDate, EndDate); + + Dictionary<Machine, List<double>> weeks_print_avg = new Dictionary<Machine, List<double>>(); + + //Init machines weeks averages dictionary. + foreach (var machine in range_job_runs.Select(x => x.Machine).OrderBy(x => x.Name).DistinctBy(x => x.Guid)) + { + weeks_print_avg[machine] = new List<double>(); + } + + //Create all available dates + List<DateTime> all_dates = range_job_runs.Select(x => x.StartDate).ToList(); + + //get first Sunday. + DateTime current_sunday = all_dates.FirstOrDefault(x => x.DayOfWeek == DayOfWeek.Sunday); + + if (current_sunday != null && all_dates.Count > 0) + { + //Iterate over each week starting from the earliest Sunday. + while (current_sunday <= all_dates.Last()) + { + var week_job_runs = range_job_runs.Where(x => x.EndPosition > 10 && x.StartDate >= current_sunday && x.StartDate <= current_sunday.AddDays(7)).ToList(); + + foreach (var machine_job_runs in week_job_runs.GroupBy(x => x.Machine)) + { + weeks_print_avg[machine_job_runs.Key].Add(machine_job_runs.Select(x => x.EndPosition).Average()); + } + + current_sunday = current_sunday.AddDays(8); + } + } + + Dictionary<Machine, double> week_print_avg = new Dictionary<Machine, double>(); + + //Init machines week average dictionary. + foreach (var machine in weeks_print_avg) + { + if (machine.Value.Count > 0) + { + week_print_avg[machine.Key] = machine.Value.Average(); + } + } + + //Init chart series + PrintPerWeekSeries = new LabeledSeriesCollection() + { + Title = "Average Printed Thread Per Week (m)", + ChartTitle = "Average Print Per Week (m)", + LabelsTitle = "Date", + SeriesColors = new List<Color>() + { + + }, + }; + + //Init series colors intensity by number of prints. + double max = week_print_avg.Count > 0 ? week_print_avg.Max(x => x.Value) : 0; + foreach (var machine in week_print_avg) + { + double a = (machine.Value / max); + PrintPerWeekSeries.SeriesColors.Add(Color.FromArgb((byte)(255d * (machine.Value / max)), 0, 200, 0)); + } + + //Init columns. + int index = 0; + + foreach (var machine in week_print_avg) + { + var series = new ColumnSeries() + { + Title = machine.Key.Name, + Values = new ChartValues<int>() { (int)machine.Value }, + Fill = new SolidColorBrush(PrintPerWeekSeries.SeriesColors[index++]), + DataLabels = true, + ToolTip = machine.Key.SerialNumber, + }; + + PrintPerWeekSeries.SeriesCollection.Add(series); + } + } + + #endregion + + #region Filter by Date + public void OnDateRangeChanged() + { + if (_job_runs != null && _job_runs.Count > 0)// && _loaded) + { + GenerateTimelineJobStatusChart(); + GeneratePieFailedReasonsChart(); + GeneratePrintPerWeekChart(); + } + } + #endregion + + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/ViewModels/JobRunsViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/ViewModels/JobRunsViewVM.cs new file mode 100644 index 000000000..f03e7f67d --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/ViewModels/JobRunsViewVM.cs @@ -0,0 +1,209 @@ +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.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; + +namespace Tango.MachineStudio.Statistics.ViewModels +{ + public class JobRunsViewVM : ViewModel + { + private INotificationProvider _notification; + private List<Machine> _allMachines; + private List<User> _allUsers; + + #region Properties + + private List<JobRunModel> _jobRuns; + public List<JobRunModel> JobRuns + { + get { return _jobRuns; } + set + { + _jobRuns = value; + RaisePropertyChangedAuto(); + } + } + + private JobRunModel _selectedJobRun = null; + public JobRunModel SelectedJobRun + { + get { return _selectedJobRun; } + set + { + _selectedJobRun = value; + RaisePropertyChangedAuto(); + } + } + + private SelectedObjectCollection<Machine> _selectedMachines; + public SelectedObjectCollection<Machine> SelectedMachines + { + get { return _selectedMachines; } + set + { + _selectedMachines = value; + RaisePropertyChangedAuto(); + } + } + + 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 _length; + public Double Length + { + get { return _length; } + set + { + _length = value; + RaisePropertyChangedAuto(); + } + } + + private JobSource _jobRunSource; + + public JobSource JobRunSource + { + get { return _jobRunSource; } + set { _jobRunSource = value; RaisePropertyChangedAuto(); } + } + + private JobRunStatus _jobRunStatus; + + public JobRunStatus JobRunStatus + { + get { return _jobRunStatus; } + set { _jobRunStatus = value; RaisePropertyChangedAuto(); } + } + + private bool? _isGradient; + + public bool? IsGradient + { + get { return _isGradient; } + set { + _isGradient = value; + RaisePropertyChangedAuto(); } + } + + private string _jobName; + + public string JobName + { + get { return _jobName; } + set { _jobName = value; } + } + + + #endregion + + public RelayCommand LoadJobRunsCommand { get; set; } + + public JobRunsViewVM(INotificationProvider notificationProvider) + { + _notification = notificationProvider; + JobRuns = new List<JobRunModel>(); + LoadJobRunsCommand = new RelayCommand( GetJobRuns); + } + + + 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.ToListAsync(); + SelectedMachines = new SelectedObjectCollection<Machine>(_allMachines.ToObservableCollection(), new ObservableCollection<Machine>()); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error loading job runs."); + } + finally + { + IsFree = true; + } + } + } + + private async void GetJobRuns() + { + await LoadJobRuns(); + } + + private async Task LoadJobRuns() + { + using (_notification.PushTaskItem("Loading job runs...")) + { + try + { + IsFree = false; + + using (var db = ObservablesContext.CreateDefault()) + { + DateTime startUtc = StartSelectedDate.ToUniversalTime(); + TimeSpan offsetTime = (EndSelectedDate.Date == DateTime.Now.Date) ? DateTime.Now.TimeOfDay : new TimeSpan(23, 59, 59); + DateTime endUtc = EndSelectedDate.ToUniversalTime() + offsetTime; + + var runs = await db.JobRuns.Select(x => new JobRunModel() + { + JobRun = x, + Machine = _allMachines.FirstOrDefault(y => y.Guid == x.MachineGuid), + User = _allUsers.SingleOrDefault(y => y.Guid == x.UserGuid), + }).ToListAsync(); + + // .Query(y => y.Where + //(x => JobName == null || + //(x.JobName.ToString().ToLower().StartsWith(JobName) + //|| Length == null || (x.JobLength == Length) + //|| IsGradient == null || (x.IsGradient != null && x.IsGradient == IsGradient) + //|| ( x.JobRunSource == JobRunSource)))) + //.BuildAsync(); + + runs.ForEach(x => x.Init()); + + JobRuns = runs; + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error loading job runs."); + } + finally + { + IsFree = true; + } + } + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/ViewModels/MainViewVM.cs index dfbfe2648..4a75a41c8 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/ViewModels/MainViewVM.cs @@ -19,349 +19,38 @@ namespace Tango.MachineStudio.Statistics.ViewModels { public class MainViewVM : StudioViewModel { - private ObservablesContext _context; - private List<JobRunModel> _job_runs; - private bool rendered; private INotificationProvider _notification; - private bool _loaded; - private LabeledSeriesCollection _timelineJobStatusSeries; - public LabeledSeriesCollection TimelineJobStatusSeries + private ChartsViewVM _chartsViewVM; + public ChartsViewVM ChartsViewVM { - get { return _timelineJobStatusSeries; } - set { _timelineJobStatusSeries = value; RaisePropertyChangedAuto(); } + get { return _chartsViewVM; } + set { _chartsViewVM = value; RaisePropertyChangedAuto(); } } - private LabeledSeriesCollection _pieJobFailedReasons; - public LabeledSeriesCollection PieJobFailedReasons + private JobRunsViewVM _jobRunsViewVM; + public JobRunsViewVM JobRunsViewVM { - get { return _pieJobFailedReasons; } - set { _pieJobFailedReasons = value; RaisePropertyChangedAuto(); } + get { return _jobRunsViewVM; } + set { _jobRunsViewVM = value; RaisePropertyChangedAuto(); } } - - private LabeledSeriesCollection _printPerWeekSeries; - public LabeledSeriesCollection PrintPerWeekSeries - { - get { return _printPerWeekSeries; } - set { _printPerWeekSeries = value; RaisePropertyChangedAuto(); } - } - - private DateTime _startDate; - public DateTime StartDate - { - get { return _startDate; } - set { _startDate = value; RaisePropertyChangedAuto(); OnDateRangeChanged(); } - } - - private DateTime _endDate; - public DateTime EndDate - { - get { return _endDate; } - set { _endDate = value; RaisePropertyChangedAuto(); OnDateRangeChanged(); } - } - - private DateTime _minDate; - public DateTime MinDate - { - get { return _minDate; } - set { _minDate = value; RaisePropertyChangedAuto(); } - } - - private DateTime _maxDate; - public DateTime MaxDate - { - get { return _maxDate; } - set { _maxDate = value; RaisePropertyChangedAuto(); } - } - + public MainViewVM(INotificationProvider notificationProvider) { _notification = notificationProvider; - - StartDate = DateTime.Now.AddMonths(-1); - EndDate = DateTime.Now; + ChartsViewVM = new ChartsViewVM(_notification); + JobRunsViewVM = new JobRunsViewVM(_notification); } public override void OnApplicationReady() { - - } - - private List<JobRunModel> GetJobRunsByDateRange(DateTime startDate, DateTime endTime, JobRunStatus? status = null) - { - return _job_runs.Where(x => x.StartDate.ToLocalTime() >= startDate && x.StartDate.ToLocalTime() <= endTime && (status == null || x.JobRunStatus == status)).ToList(); - } - - private List<JobRunModel> GetJobRunsByDate(DateTime date, JobRunStatus? status = null) - { - return _job_runs.Where(x => x.StartDate.ToLocalTime().Date == date.Date && (status == null || x.JobRunStatus == status)).ToList(); - } - - private IEnumerable<DateTime> CreateDates(DateTime start, DateTime end) - { - for (DateTime date = start.Date; date.Date <= end.Date; date = date.AddDays(1)) - { - yield return date; - } + JobRunsViewVM.Init(); } - public override async void OnNavigatedTo() + public override void OnNavigatedTo() { base.OnNavigatedTo(); - - if (rendered) return; - - rendered = true; - - - using (_notification.PushTaskItem("Loading statistics...")) - { - IsFree = false; - - await Task.Factory.StartNew(() => - { - _context = ObservablesContext.CreateDefault(); - _job_runs = _context.JobRuns.OrderBy(x => x.StartDate).ToList().Select(x => new JobRunModel(x)).ToList(); - foreach (var run in _job_runs) - { - run.LoadMachine(_context).GetAwaiter().GetResult(); - } - }); - - if (_job_runs.Count > 0) - { - MinDate = _job_runs.Min(x => x.StartDate); - MaxDate = _job_runs.Max(x => x.StartDate); - } - - InvokeUIOnIdle(() => - { - OnDateRangeChanged(); - }); - - _loaded = true; - - IsFree = true; - } - } - - private void OnDateRangeChanged() - { - if (_job_runs != null && _job_runs.Count > 0 && _loaded) - { - GenerateTimelineJobStatusChart(); - GeneratePieFailedReasonsChart(); - GeneratePrintPerWeekChart(); - } - } - - private void GenerateTimelineJobStatusChart() - { - TimelineJobStatusSeries = new LabeledSeriesCollection() - { - Title = "Job Runs Status", - ChartTitle = "Number Of Runs", - LabelsTitle = "Date", - SeriesColors = new List<Color>() - { - Colors.Green, - Colors.Orange, - Colors.Red, - }, - }; - - Series completed_job_runs = new ColumnSeries() - { - Title = "Completed", - Values = new ChartValues<int>(), - Fill = Brushes.Green, - MinWidth = 1, - - }; - Series aborted_job_runs = new ColumnSeries() - { - Title = "Aborted", - Values = new ChartValues<int>(), - Fill = Brushes.Orange, - MinWidth = 1, - }; - Series failed_job_runs = new ColumnSeries() - { - Title = "Failed", - Values = new ChartValues<int>(), - Fill = Brushes.Red, - MinWidth = 1, - }; - - if (EndDate - StartDate > TimeSpan.FromDays(40)) - { - completed_job_runs = new LineSeries() - { - Title = "Completed", - Values = new ChartValues<int>(), - Fill = new SolidColorBrush(Colors.Green) { Opacity = 0.5 }, - MinWidth = 1, - PointGeometry = null, - StrokeThickness = 0, - - }; - aborted_job_runs = new LineSeries() - { - Title = "Aborted", - Values = new ChartValues<int>(), - Fill = new SolidColorBrush(Colors.Orange) { Opacity = 0.5 }, - MinWidth = 1, - PointGeometry = null, - StrokeThickness = 0, - }; - failed_job_runs = new LineSeries() - { - Title = "Failed", - Values = new ChartValues<int>(), - Fill = new SolidColorBrush(Colors.Red) { Opacity = 0.5 }, - MinWidth = 1, - PointGeometry = null, - StrokeThickness = 0, - }; - } - - foreach (var date in CreateDates(StartDate, EndDate)) - { - completed_job_runs.Values.Add(GetJobRunsByDate(date, JobRunStatus.Completed).Count()); - aborted_job_runs.Values.Add(GetJobRunsByDate(date, JobRunStatus.Aborted).Count()); - failed_job_runs.Values.Add(GetJobRunsByDate(date, JobRunStatus.Failed).Count()); - - TimelineJobStatusSeries.Labels.Add(date.ToShortDateString()); - } - - - - TimelineJobStatusSeries.SeriesCollection.Add(failed_job_runs); - TimelineJobStatusSeries.SeriesCollection.Add(aborted_job_runs); - TimelineJobStatusSeries.SeriesCollection.Add(completed_job_runs); - } - - private void GeneratePieFailedReasonsChart() - { - var groups = GetJobRunsByDateRange(StartDate, EndDate, JobRunStatus.Failed).GroupBy(x => x.FailedMessage).OrderBy(x => x.Count()); - - List<Color> colors = new List<Color>(); - - int max = groups.Count() > 0 ? groups.Max(x => x.Count()) : 0; - - for (int i = 0; i < groups.Count(); i++) - { - int count = groups.ElementAt(i).Count(); - double alpha = Math.Max(((double)(count) / max * 200), 20); - colors.Add(Color.FromArgb((byte)alpha, 200, 0, 0)); - } - - PieJobFailedReasons = new LabeledSeriesCollection() - { - Title = "Job Failure Reasons", - SeriesColors = colors, - }; - - int index = 0; - - foreach (var group in groups) - { - int count = group.Count(); - - var series = new PieSeries() - { - Title = group.First().FailedMessage, - Values = new ChartValues<int>() { count }, - Fill = new SolidColorBrush(colors[index++]), - DataLabels = true, - ToolTip = group.First().FailedMessage, - }; - - PieJobFailedReasons.SeriesCollection.Add(series); - } - } - - private void GeneratePrintPerWeekChart() - { - List<JobRunModel> range_job_runs = GetJobRunsByDateRange(StartDate, EndDate); - - Dictionary<Machine, List<double>> weeks_print_avg = new Dictionary<Machine, List<double>>(); - - //Init machines weeks averages dictionary. - foreach (var machine in range_job_runs.Select(x => x.Machine).OrderBy(x => x.Name).DistinctBy(x => x.Guid)) - { - weeks_print_avg[machine] = new List<double>(); - } - - //Create all available dates - List<DateTime> all_dates = range_job_runs.Select(x => x.StartDate).ToList(); - - //get first Sunday. - DateTime current_sunday = all_dates.FirstOrDefault(x => x.DayOfWeek == DayOfWeek.Sunday); - - if (current_sunday != null && all_dates.Count > 0) - { - //Iterate over each week starting from the earliest Sunday. - while (current_sunday <= all_dates.Last()) - { - var week_job_runs = range_job_runs.Where(x => x.EndPosition > 10 && x.StartDate >= current_sunday && x.StartDate <= current_sunday.AddDays(7)).ToList(); - - foreach (var machine_job_runs in week_job_runs.GroupBy(x => x.Machine)) - { - weeks_print_avg[machine_job_runs.Key].Add(machine_job_runs.Select(x => x.EndPosition).Average()); - } - - current_sunday = current_sunday.AddDays(8); - } - } - - Dictionary<Machine, double> week_print_avg = new Dictionary<Machine, double>(); - - //Init machines week average dictionary. - foreach (var machine in weeks_print_avg) - { - if (machine.Value.Count > 0) - { - week_print_avg[machine.Key] = machine.Value.Average(); - } - } - - //Init chart series - PrintPerWeekSeries = new LabeledSeriesCollection() - { - Title = "Average Printed Thread Per Week (m)", - ChartTitle = "Average Print Per Week (m)", - LabelsTitle = "Date", - SeriesColors = new List<Color>() - { - - }, - }; - - //Init series colors intensity by number of prints. - double max = week_print_avg.Count > 0 ? week_print_avg.Max(x => x.Value) : 0; - foreach (var machine in week_print_avg) - { - double a = (machine.Value / max); - PrintPerWeekSeries.SeriesColors.Add(Color.FromArgb((byte)(255d * (machine.Value / max)), 0, 200, 0)); - } - - //Init columns. - int index = 0; - - foreach (var machine in week_print_avg) - { - var series = new ColumnSeries() - { - Title = machine.Key.Name, - Values = new ChartValues<int>() { (int)machine.Value }, - Fill = new SolidColorBrush(PrintPerWeekSeries.SeriesColors[index++]), - DataLabels = true, - ToolTip = machine.Key.SerialNumber, - }; - - PrintPerWeekSeries.SeriesCollection.Add(series); - } + ChartsViewVM.Init(); } } } diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/ChartsView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/ChartsView.xaml new file mode 100644 index 000000000..ecd7e01ed --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/ChartsView.xaml @@ -0,0 +1,110 @@ +<UserControl x:Class="Tango.MachineStudio.Statistics.Views.ChartsView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:vm="clr-namespace:Tango.MachineStudio.Statistics.ViewModels" + xmlns:global="clr-namespace:Tango.MachineStudio.Statistics" + xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:local="clr-namespace:Tango.MachineStudio.Statistics.Views" + xmlns:tooltips="clr-namespace:Tango.MachineStudio.Statistics.Tooltips" + xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf" + mc:Ignorable="d" + d:DesignHeight="450" d:DesignWidth="800"> + <Grid IsEnabled="{Binding IsFree}"> + <Grid.RowDefinitions> + <RowDefinition Height="80"/> + <RowDefinition Height="337*"/> + </Grid.RowDefinitions> + + <StackPanel Margin="60 20 0 0" VerticalAlignment="Center" HorizontalAlignment="Left" Orientation="Horizontal"> + <StackPanel> + <TextBlock FontSize="10">Start Date:</TextBlock> + <DatePicker DisplayDateStart="{Binding MinDate}" DisplayDateEnd="{Binding MaxDate}" SelectedDate="{Binding StartDate}" materialDesign:HintAssist.Hint="Pick start date" Width="200" VerticalAlignment="Bottom" FontSize="16"></DatePicker> + </StackPanel> + + <StackPanel Margin="40 0 0 0"> + <TextBlock FontSize="10">End Date:</TextBlock> + <DatePicker DisplayDateStart="{Binding MinDate}" DisplayDateEnd="{Binding MaxDate}" SelectedDate="{Binding EndDate}" materialDesign:HintAssist.Hint="Pick end date" Width="200" VerticalAlignment="Bottom" FontSize="16"></DatePicker> + </StackPanel> + </StackPanel> + + <UniformGrid Columns="3" Margin="50 20 50 50" Rows="2" Grid.Row="1"> + <Border BorderBrush="{StaticResource Statistics.BorderBrush}" Padding="5" BorderThickness="1" CornerRadius="5" Margin="10"> + <DockPanel> + <TextBlock DockPanel.Dock="Top" FontSize="16" FontWeight="Bold" HorizontalAlignment="Center" Padding="10" Text="{Binding TimelineJobStatusSeries.Title}"></TextBlock> + <lvc:CartesianChart Series="{Binding TimelineJobStatusSeries.SeriesCollection}" LegendLocation="Bottom" SeriesColors="{Binding TimelineJobStatusSeries.SeriesColors}"> + <lvc:CartesianChart.DataTooltip> + <lvc:DefaultTooltip BulletSize="20" Background="{StaticResource TransparentBackgroundBrush450}" Foreground="{StaticResource Dialog.Foreground}"/> + </lvc:CartesianChart.DataTooltip> + <lvc:CartesianChart.AxisX> + <lvc:Axis Title="{Binding TimelineJobStatusSeries.LabelsTitle}" Labels="{Binding TimelineJobStatusSeries.Labels}" Foreground="{StaticResource DarkGrayBrush200}"> + <lvc:Axis.Separator> + <lvc:Separator Stroke="#A5A5A5" /> + </lvc:Axis.Separator> + </lvc:Axis> + </lvc:CartesianChart.AxisX> + <lvc:CartesianChart.AxisY> + <lvc:Axis Title="{Binding TimelineJobStatusSeries.ChartTitle}" MinValue="0" Foreground="{StaticResource DarkGrayBrush200}" > + <lvc:Axis.Separator> + <lvc:Separator Stroke="#B0B0B0" /> + </lvc:Axis.Separator> + </lvc:Axis> + </lvc:CartesianChart.AxisY> + </lvc:CartesianChart> + </DockPanel> + </Border> + + <Border BorderBrush="{StaticResource Statistics.BorderBrush}" Padding="5" BorderThickness="1" CornerRadius="5" Margin="10"> + <DockPanel> + <TextBlock DockPanel.Dock="Top" FontSize="16" FontWeight="Bold" HorizontalAlignment="Center" Padding="10" Text="{Binding PieJobFailedReasons.Title}"></TextBlock> + <UniformGrid Columns="2"> + <lvc:PieChart DataHover="PieChart_DataHover" Series="{Binding PieJobFailedReasons.SeriesCollection}" LegendLocation="None" SeriesColors="{Binding PieJobFailedReasons.SeriesColors}" Background="Transparent"> + <lvc:PieChart.Resources> + <Style TargetType="lvc:PieSeries"> + <Setter Property="Stroke" Value="#99F9F9F9"></Setter> + <Setter Property="StrokeThickness" Value="2"/> + </Style> + </lvc:PieChart.Resources> + <lvc:PieChart.DataTooltip> + <tooltips:PieChartTooltipControl Visibility="Hidden" /> + </lvc:PieChart.DataTooltip> + </lvc:PieChart> + + <TextBlock x:Name="txtPieTitle" TextWrapping="Wrap"></TextBlock> + </UniformGrid> + </DockPanel> + </Border> + + <Border BorderBrush="{StaticResource Statistics.BorderBrush}" Padding="5" BorderThickness="1" CornerRadius="5" Margin="10"> + <DockPanel> + <TextBlock DockPanel.Dock="Top" FontSize="16" FontWeight="Bold" HorizontalAlignment="Center" Padding="10" Text="{Binding PrintPerWeekSeries.Title}"></TextBlock> + <lvc:CartesianChart Series="{Binding PrintPerWeekSeries.SeriesCollection}" LegendLocation="Bottom" SeriesColors="{Binding PrintPerWeekSeries.SeriesColors}" > + <lvc:CartesianChart.Resources> + <Style TargetType="lvc:ColumnSeries"> + <Setter Property="Foreground" Value="{StaticResource DarkGrayBrush200}"></Setter> + </Style> + </lvc:CartesianChart.Resources> + <lvc:CartesianChart.AxisX> + <lvc:Axis Title="{Binding PrintPerWeekSeries.LabelsTitle}" Labels="{Binding PrintPerWeekSeries.Labels}" Foreground="{StaticResource DarkGrayBrush200}"> + <lvc:Axis.Separator> + <lvc:Separator Stroke="#A5A5A5" /> + </lvc:Axis.Separator> + </lvc:Axis> + </lvc:CartesianChart.AxisX> + <lvc:CartesianChart.AxisY> + <lvc:Axis Title="{Binding PrintPerWeekSeries.ChartTitle}" MinValue="0" Foreground="{StaticResource DarkGrayBrush200}" > + <lvc:Axis.Separator> + <lvc:Separator Stroke="#6F6F6F" /> + </lvc:Axis.Separator> + </lvc:Axis> + </lvc:CartesianChart.AxisY> + <lvc:CartesianChart.DataTooltip> + <lvc:DefaultTooltip BulletSize="20" Background="{StaticResource TransparentBackgroundBrush450}" Foreground="{StaticResource Dialog.Foreground}"/> + </lvc:CartesianChart.DataTooltip> + </lvc:CartesianChart> + </DockPanel> + </Border> + </UniformGrid> + </Grid> +</UserControl> diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/ChartsView.xaml.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/ChartsView.xaml.cs new file mode 100644 index 000000000..d79d88281 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/ChartsView.xaml.cs @@ -0,0 +1,35 @@ +using LiveCharts; +using LiveCharts.Wpf; +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.Statistics.Views +{ + /// <summary> + /// Interaction logic for ChartsView.xaml + /// </summary> + public partial class ChartsView : UserControl + { + public ChartsView() + { + InitializeComponent(); + } + private void PieChart_DataHover(object sender, ChartPoint chartPoint) + { + var tooltip = ((chartPoint.ChartView as PieChart).DataTooltip as Tooltips.PieChartTooltipControl).Title; + txtPieTitle.Text = tooltip; + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/JobRunsView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/JobRunsView.xaml new file mode 100644 index 000000000..4667959b8 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/JobRunsView.xaml @@ -0,0 +1,239 @@ +<UserControl x:Class="Tango.MachineStudio.Statistics.Views.JobRunsView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:sharedConverters="clr-namespace:Tango.SharedUI.Converters;assembly=Tango.SharedUI" + xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:localConverters="clr-namespace:Tango.MachineStudio.Statistics.Converters" + xmlns:mahapps="http://metro.mahapps.com/winfx/xaml/controls" + xmlns:sys="clr-namespace:System;assembly=mscorlib" + xmlns:enumerations="clr-namespace:Tango.BL.Enumerations;assembly=Tango.BL" + xmlns:local="clr-namespace:Tango.MachineStudio.Statistics.Views" + mc:Ignorable="d" + d:DesignHeight="450" d:DesignWidth="1200"> + <UserControl.Resources> + <sharedConverters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> + <sharedConverters:EnumToDescriptionConverter x:Key="EnumToDescriptionConverter" /> + <sharedConverters:DateTimeUTCToShortDateTimeConverter x:Key="DateTimeUTCToShortDateTimeConverter"/> + <localConverters:DateIsInListToBooleanConverter x:Key="DateIsInListToBooleanConverter"/> + <localConverters:StringToBoolYesNoNullConverter x:Key="StringToBoolYesNoNullConverter"/> + + <ResourceDictionary x:Key="SelectAllTextBoxResource"> + <Style TargetType="TextBox"> + <EventSetter Event="GotFocus" Handler="TextBox_GotFocus"></EventSetter> + <EventSetter Event="MouseDown" Handler="TextBox_PreviewMouseUp"></EventSetter> + </Style> + </ResourceDictionary> + + <Style x:Key="CustomCalendarDayButtonStyle" TargetType="{x:Type CalendarDayButton}" BasedOn="{StaticResource MaterialDesignCalendarDayButton}"> + <Style.Triggers> + <DataTrigger Value="True"> + <DataTrigger.Binding> + <MultiBinding Converter="{StaticResource DateIsInListToBooleanConverter}"> + <Binding /> + <Binding RelativeSource="{RelativeSource AncestorType=UserControl}" Path="DataContext.Dates" /> + <Binding RelativeSource="{RelativeSource AncestorType=DatePicker}" Path="IsDropDownOpen" ></Binding> + </MultiBinding> + </DataTrigger.Binding> + <Setter Property="Foreground" Value="{StaticResource AccentColorBrush}" /> + </DataTrigger> + </Style.Triggers> + </Style> + + <Style x:Key="HighlightDatePickerStyle" TargetType="{x:Type Calendar}" BasedOn="{StaticResource {x:Type Calendar}}"> + <Setter Property="CalendarDayButtonStyle" Value="{StaticResource CustomCalendarDayButtonStyle}" /> + </Style> + + <ObjectDataProvider x:Key="JobSource" MethodName="GetValues" ObjectType="{x:Type sys:Enum}"> + <ObjectDataProvider.MethodParameters> + <x:Type TypeName="enumerations:JobSource"/> + </ObjectDataProvider.MethodParameters> + </ObjectDataProvider> + + <ObjectDataProvider x:Key="JobRunStatus" MethodName="GetValues" ObjectType="{x:Type sys:Enum}"> + <ObjectDataProvider.MethodParameters> + <x:Type TypeName="enumerations:JobRunStatus"/> + </ObjectDataProvider.MethodParameters> + </ObjectDataProvider> + + </UserControl.Resources> + + <Grid IsEnabled="{Binding IsFree}"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="3*"/> + <ColumnDefinition Width="1*"/> + </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition Height="100"/> + <RowDefinition Height="*"/> + <RowDefinition Height="Auto"/> + </Grid.RowDefinitions> + <materialDesign:Card Grid.Row="0" Background="{StaticResource WhiteBackgroundBrush}" Foreground="{StaticResource JobFieldForeground}" Width="Auto" Margin="10 10 0 0" > + + <DockPanel> + <StackPanel Orientation="Horizontal" DockPanel.Dock="Left"> + <StackPanel Margin="30 5 0 20" Orientation="Vertical" > + <TextBlock DockPanel.Dock="Top" Text="Machine:" VerticalAlignment="Center" FontSize="10"></TextBlock> + <ToggleButton Width="200" Margin="0 10 0 0" x:Name="selectMachineButton" HorizontalAlignment="Left" FontSize="16" VerticalAlignment="Center"> + <ToggleButton.Template> + <ControlTemplate> + <Grid> + <Border CornerRadius="3" BorderBrush="{StaticResource borderBrush}" BorderThickness="1" TextElement.Foreground="Black" > + <DockPanel> + <Button Width="18" Padding="0" Height="16" DockPanel.Dock="Right" BorderBrush="{x:Null}" HorizontalAlignment="Left" Click="Button_Click" Style="{StaticResource MaterialDesignFlatButton}" Foreground="{StaticResource MainWindow.Foreground}"> + <materialDesign:PackIcon Width="16" Height="16" Kind="ChevronDown" VerticalAlignment="Center" Foreground="{StaticResource MainWindow.Foreground}" HorizontalAlignment="Right"/> + </Button> + <TextBlock VerticalAlignment="Center" Foreground="{StaticResource MainWindow.Foreground}"> + <Run Text=" Select Machines"></Run> + <Run>(</Run><Run Text="{Binding SelectedMachines.SynchedSource.Count,Mode=OneWay}"></Run><Run>)</Run> + </TextBlock> + </DockPanel> + </Border> + <Popup StaysOpen="False" IsOpen="{Binding RelativeSource={RelativeSource AncestorType=ToggleButton},Path=IsChecked }" > + <Border Padding="5" Background="{DynamicResource ComboBox.Floating.Background}" BorderBrush="{StaticResource AutoCompleteTextBox.Popup.BorderBrush}"> + <ScrollViewer MaxHeight="600" Background="{DynamicResource ComboBox.Floating.Background}"> + <ItemsControl ItemsSource="{Binding SelectedMachines}" Foreground="{StaticResource MainWindow.Foreground}"> + <ItemsControl.ItemTemplate> + <DataTemplate> + <DockPanel Margin="2"> + <CheckBox VerticalAlignment="Center" DockPanel.Dock="Left" IsChecked="{Binding IsSelected}" /> + <TextBlock Margin="5 0 0 0" VerticalAlignment="Center" Text="{Binding Data.SerialNumber}"></TextBlock> + </DockPanel> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </ScrollViewer> + </Border> + </Popup> + </Grid> + </ControlTemplate> + </ToggleButton.Template> + </ToggleButton> + + </StackPanel> + <Border Margin="30 10 0 20" Padding="5" BorderBrush="#38696969" BorderThickness="1" > + <StackPanel Orientation="Horizontal"> + <materialDesign:PackIcon Kind="Calendar" Width="24" Height="24" VerticalAlignment="Bottom" Margin="0 0 0 5" /> + <DatePicker x:Name="datePicker" DisplayDateStart="{Binding MinDate}" DisplayDateEnd="{Binding MaxDate}" CalendarStyle="{StaticResource HighlightDatePickerStyle}" SelectedDate="{Binding SelectedDate}" materialDesign:HintAssist.Hint="Pick Date" Margin="10 0 0 5" Width="100" VerticalAlignment="Bottom" FontSize="16"> + </DatePicker> + </StackPanel> + </Border> + <StackPanel Margin="30 5 20 20" Orientation="Vertical"> + <TextBlock Text="Job Name:" VerticalAlignment="Center" FontSize="10"></TextBlock> + <TextBox Text="{Binding JobName}" VerticalAlignment="Center" FontSize="16" Margin="0 5 0 0" Width="100"></TextBox> + </StackPanel> + <StackPanel Margin="30 5 20 0" Orientation="Vertical"> + <TextBlock Text="Source:" VerticalAlignment="Center" FontSize="10"></TextBlock> + <ComboBox Width="100" FontSize="14" Margin="0 5 0 0" ItemsSource="{Binding Source={StaticResource JobSource}}" SelectedItem="{Binding JobRunSource, UpdateSourceTrigger=PropertyChanged}"> + <ComboBox.ItemContainerStyle> + <Style TargetType="ComboBoxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}"> + <Setter Property="Background" Value="{StaticResource WhiteBrush100}"></Setter> + </Style> + </ComboBox.ItemContainerStyle> + <ComboBox.ItemTemplate> + <DataTemplate> + <TextBlock Text="{Binding Converter={StaticResource EnumToDescriptionConverter}}"></TextBlock> + </DataTemplate> + </ComboBox.ItemTemplate> + </ComboBox> + </StackPanel> + <StackPanel Margin="30 5 20 0" Orientation="Vertical"> + <TextBlock Text="Gradient:" VerticalAlignment="Center" FontSize="10"></TextBlock> + <ComboBox Width="100" FontSize="14" Margin="0 5 0 0" SelectedItem="{Binding IsGradient, Converter={StaticResource StringToBoolYesNoNullConverter}, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"> + <ComboBox.ItemContainerStyle> + <Style TargetType="ComboBoxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}"> + <Setter Property="Background" Value="{StaticResource WhiteBrush100}"></Setter> + </Style> + </ComboBox.ItemContainerStyle> + <ComboBoxItem Content="Yes" /> + <ComboBoxItem Content="No" /> + <ComboBoxItem Content="Null" /> + </ComboBox> + </StackPanel> + <StackPanel Margin="30 5 20 0" Orientation="Vertical"> + <TextBlock Text="Length:" VerticalAlignment="Center" FontSize="10"></TextBlock> + <mahapps:NumericUpDown Width="100" Margin="0 8.5 0 0" x:Name="numLength" FontSize="21" HideUpDownButtons="True" HorizontalAlignment="Left" FontFamily="{StaticResource digital-7}" StringFormat="{}{0:N1} m" Minimum="0" Maximum="5000" InterceptArrowKeys="True" Background="Transparent" BorderThickness="0 0 0 1" BorderBrush="{StaticResource DimGrayBrush}" InterceptMouseWheel="True" HasDecimals="True" HorizontalContentAlignment="Left" Value="{Binding Length,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"> + <mahapps:NumericUpDown.Resources> + <StaticResource ResourceKey="SelectAllTextBoxResource"></StaticResource> + </mahapps:NumericUpDown.Resources> + </mahapps:NumericUpDown> + </StackPanel> + <StackPanel Margin="30 5 20 0" Orientation="Vertical"> + <TextBlock Text="Status:" VerticalAlignment="Center" FontSize="10"></TextBlock> + <ComboBox Width="100" FontSize="14" Margin="0 5 0 0" ItemsSource="{Binding Source={StaticResource JobRunStatus}}" SelectedItem="{Binding JobRunStatus, UpdateSourceTrigger=PropertyChanged}"> + <ComboBox.ItemContainerStyle> + <Style TargetType="ComboBoxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}"> + <Setter Property="Background" Value="{StaticResource WhiteBrush100}"></Setter> + </Style> + </ComboBox.ItemContainerStyle> + <ComboBox.ItemTemplate> + <DataTemplate> + <TextBlock Text="{Binding Converter={StaticResource EnumToDescriptionConverter}}"></TextBlock> + </DataTemplate> + </ComboBox.ItemTemplate> + </ComboBox> + </StackPanel> + + </StackPanel> + <Button HorizontalAlignment="Right" Command="{Binding LoadJobRunsCommand}" DockPanel.Dock="Right" Margin="10">GO</Button> + </DockPanel> + </materialDesign:Card> + + <DataGrid Grid.Row="1" Margin="20 0 0 10" SelectionMode="Single" SelectionUnit="FullRow" RowHeight="40" BorderBrush="{StaticResource borderBrush}" IsReadOnly="True" BorderThickness="1" + Background="{StaticResource TransparentBackgroundBrush}" AlternatingRowBackground="{StaticResource Transparent200}" AutoGenerateColumns="False" CanUserReorderColumns="True" + CanUserAddRows="False" CanUserDeleteRows="False" ItemsSource="{Binding JobRuns}" + SelectedItem="{Binding SelectedJobRun}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" FontSize="11"> + <DataGrid.CellStyle> + <Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}"> + <Setter Property="BorderThickness" Value="0"/> + <Setter Property="FocusVisualStyle" Value="{x:Null}"/> + <Setter Property="VerticalContentAlignment" Value="Center"></Setter> + <Style.Triggers> + <Trigger Property="IsSelected" Value="True"> + <Setter Property="Background" Value="Transparent"></Setter> + <Setter Property="Foreground" Value="{StaticResource AccentColorBrush}" /> + </Trigger> + </Style.Triggers> + </Style> + </DataGrid.CellStyle> + <DataGrid.RowStyle> + <Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}"> + <Style.Triggers> + <Trigger Property="IsMouseOver" Value="True"> + <Setter Property="Background" Value="Transparent"></Setter> + <Setter Property="Foreground" Value="{StaticResource AccentColorBrush}" /> + <Setter Property="Cursor" Value="Hand"></Setter> + </Trigger> + <Trigger Property="IsSelected" Value="True"> + <Setter Property="Background" Value="Transparent"></Setter> + </Trigger> + <Trigger Property="IsFocused" Value="True"> + <Setter Property="Background" Value="Transparent"></Setter> + </Trigger> + </Style.Triggers> + </Style> + </DataGrid.RowStyle> + + <DataGrid.Columns> + <DataGridTextColumn Header="START DATE" Binding="{Binding JobRun.StartDate, Converter={StaticResource DateTimeUTCToShortDateTimeConverter}}" Width="Auto" /> + <DataGridTextColumn Header="LENGTH" Binding="{Binding JobRun.JobLength}" Width="Auto" /> + <DataGridTextColumn Header="STATUS" Binding="{Binding JobRun.JobRunStatus, Converter={StaticResource EnumToDescriptionConverter}}" Width="Auto"/> + <DataGridTextColumn Header="DESIGNATION" Binding="{Binding JobRun.Designation, Converter={StaticResource EnumToDescriptionConverter}}" Width="Auto"/> + <DataGridTextColumn Header="SOURCE" Binding="{Binding JobRun.Source, Converter={StaticResource EnumToDescriptionConverter}}" Width="Auto" /> + </DataGrid.Columns> + </DataGrid> + <Border Grid.Row="1" Grid.Column="1" Margin="20, 0, 20, 10" BorderBrush="{StaticResource borderBrush}" Background="{StaticResource TransparentBackgroundBrush}" BorderThickness="1"> + + <StackPanel> + <TextBlock Margin="10" Text="{Binding SelectedJobRunsText}"></TextBlock> + </StackPanel> + </Border> + <Border Grid.Row="2" Grid.ColumnSpan="2" Margin="20, 10, 20, 20" BorderBrush="{StaticResource borderBrush}" Background="{StaticResource TransparentBackgroundBrush}" BorderThickness="1"> + <StackPanel Background="{StaticResource TransparentBackgroundBrush}" > + <TextBlock>TOTALS:</TextBlock> + <TextBlock>Cyan:</TextBlock> + </StackPanel> + </Border> + </Grid> +</UserControl> diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/JobRunsView.xaml.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/JobRunsView.xaml.cs new file mode 100644 index 000000000..0966533b9 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/JobRunsView.xaml.cs @@ -0,0 +1,44 @@ +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.Statistics.Views +{ + /// <summary> + /// Interaction logic for JobRunsView.xaml + /// </summary> + public partial class JobRunsView : UserControl + { + public JobRunsView() + { + InitializeComponent(); + } + private void Button_Click(object sender, RoutedEventArgs e) + { + selectMachineButton.IsChecked = true; + e.Handled = true; + } + private async void TextBox_GotFocus(object sender, RoutedEventArgs e) + { + await Task.Delay(200); + TextBox txtBox = sender as TextBox; + txtBox.SelectAll(); + } + + private void TextBox_PreviewMouseUp(object sender, MouseButtonEventArgs e) + { + e.Handled = true; + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/MainView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/MainView.xaml index 55804c7b3..170d3276a 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/MainView.xaml +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/MainView.xaml @@ -11,100 +11,28 @@ xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf" mc:Ignorable="d" d:DesignHeight="1080" d:DesignWidth="1920" Background="Transparent" d:DataContext="{d:DesignInstance Type=vm:MainViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.MainViewVM}"> - <Grid IsEnabled="{Binding IsFree}"> + <Grid> <Grid.RowDefinitions> - <RowDefinition Height="80"/> - <RowDefinition Height="337*"/> + <RowDefinition Height="1*"/> + <RowDefinition Height="20"/> </Grid.RowDefinitions> - - <StackPanel Margin="60 20 0 0" VerticalAlignment="Center" HorizontalAlignment="Left" Orientation="Horizontal"> - <StackPanel> - <TextBlock FontSize="10">Start Date:</TextBlock> - <DatePicker DisplayDateStart="{Binding MinDate}" DisplayDateEnd="{Binding MaxDate}" SelectedDate="{Binding StartDate}" materialDesign:HintAssist.Hint="Pick start date" Width="200" VerticalAlignment="Bottom" FontSize="16"></DatePicker> - </StackPanel> - - <StackPanel Margin="40 0 0 0"> - <TextBlock FontSize="10">End Date:</TextBlock> - <DatePicker DisplayDateStart="{Binding MinDate}" DisplayDateEnd="{Binding MaxDate}" SelectedDate="{Binding EndDate}" materialDesign:HintAssist.Hint="Pick end date" Width="200" VerticalAlignment="Bottom" FontSize="16"></DatePicker> - </StackPanel> - </StackPanel> - - <UniformGrid Columns="3" Margin="50 20 50 50" Rows="2" Grid.Row="1"> - <Border BorderBrush="{StaticResource Statistics.BorderBrush}" Padding="5" BorderThickness="1" CornerRadius="5" Margin="10"> - <DockPanel> - <TextBlock DockPanel.Dock="Top" FontSize="16" FontWeight="Bold" HorizontalAlignment="Center" Padding="10" Text="{Binding TimelineJobStatusSeries.Title}"></TextBlock> - <lvc:CartesianChart Series="{Binding TimelineJobStatusSeries.SeriesCollection}" LegendLocation="Bottom" SeriesColors="{Binding TimelineJobStatusSeries.SeriesColors}"> - <lvc:CartesianChart.DataTooltip> - <lvc:DefaultTooltip BulletSize="20" Background="{StaticResource TransparentBackgroundBrush450}" Foreground="{StaticResource Dialog.Foreground}"/> - </lvc:CartesianChart.DataTooltip> - <lvc:CartesianChart.AxisX> - <lvc:Axis Title="{Binding TimelineJobStatusSeries.LabelsTitle}" Labels="{Binding TimelineJobStatusSeries.Labels}" Foreground="{StaticResource DarkGrayBrush200}"> - <lvc:Axis.Separator> - <lvc:Separator Stroke="#A5A5A5" /> - </lvc:Axis.Separator> - </lvc:Axis> - </lvc:CartesianChart.AxisX> - <lvc:CartesianChart.AxisY> - <lvc:Axis Title="{Binding TimelineJobStatusSeries.ChartTitle}" MinValue="0" Foreground="{StaticResource DarkGrayBrush200}" > - <lvc:Axis.Separator> - <lvc:Separator Stroke="#B0B0B0" /> - </lvc:Axis.Separator> - </lvc:Axis> - </lvc:CartesianChart.AxisY> - </lvc:CartesianChart> - </DockPanel> - </Border> - - <Border BorderBrush="{StaticResource Statistics.BorderBrush}" Padding="5" BorderThickness="1" CornerRadius="5" Margin="10"> - <DockPanel> - <TextBlock DockPanel.Dock="Top" FontSize="16" FontWeight="Bold" HorizontalAlignment="Center" Padding="10" Text="{Binding PieJobFailedReasons.Title}"></TextBlock> - <UniformGrid Columns="2"> - <lvc:PieChart DataHover="PieChart_DataHover" Series="{Binding PieJobFailedReasons.SeriesCollection}" LegendLocation="None" SeriesColors="{Binding PieJobFailedReasons.SeriesColors}" Background="Transparent"> - <lvc:PieChart.Resources> - <Style TargetType="lvc:PieSeries"> - <Setter Property="Stroke" Value="#99F9F9F9"></Setter> - <Setter Property="StrokeThickness" Value="2"/> - </Style> - </lvc:PieChart.Resources> - <lvc:PieChart.DataTooltip> - <tooltips:PieChartTooltipControl Visibility="Hidden" /> - </lvc:PieChart.DataTooltip> - </lvc:PieChart> - - <TextBlock x:Name="txtPieTitle" TextWrapping="Wrap"></TextBlock> - </UniformGrid> - </DockPanel> - </Border> - - <Border BorderBrush="{StaticResource Statistics.BorderBrush}" Padding="5" BorderThickness="1" CornerRadius="5" Margin="10"> - <DockPanel> - <TextBlock DockPanel.Dock="Top" FontSize="16" FontWeight="Bold" HorizontalAlignment="Center" Padding="10" Text="{Binding PrintPerWeekSeries.Title}"></TextBlock> - <lvc:CartesianChart Series="{Binding PrintPerWeekSeries.SeriesCollection}" LegendLocation="Bottom" SeriesColors="{Binding PrintPerWeekSeries.SeriesColors}" > - <lvc:CartesianChart.Resources> - <Style TargetType="lvc:ColumnSeries"> - <Setter Property="Foreground" Value="{StaticResource DarkGrayBrush200}"></Setter> - </Style> - </lvc:CartesianChart.Resources> - <lvc:CartesianChart.AxisX> - <lvc:Axis Title="{Binding PrintPerWeekSeries.LabelsTitle}" Labels="{Binding PrintPerWeekSeries.Labels}" Foreground="{StaticResource DarkGrayBrush200}"> - <lvc:Axis.Separator> - <lvc:Separator Stroke="#A5A5A5" /> - </lvc:Axis.Separator> - </lvc:Axis> - </lvc:CartesianChart.AxisX> - <lvc:CartesianChart.AxisY> - <lvc:Axis Title="{Binding PrintPerWeekSeries.ChartTitle}" MinValue="0" Foreground="{StaticResource DarkGrayBrush200}" > - <lvc:Axis.Separator> - <lvc:Separator Stroke="#6F6F6F" /> - </lvc:Axis.Separator> - </lvc:Axis> - </lvc:CartesianChart.AxisY> - <lvc:CartesianChart.DataTooltip> - <lvc:DefaultTooltip BulletSize="20" Background="{StaticResource TransparentBackgroundBrush450}" Foreground="{StaticResource Dialog.Foreground}"/> - </lvc:CartesianChart.DataTooltip> - </lvc:CartesianChart> - </DockPanel> - </Border> - </UniformGrid> + <Grid Grid.Row="0" IsEnabled="{Binding IsFree}"> + <TabControl Background="Transparent" Margin="0,40,0,0" x:Name="chartsTabControl" Padding="0 25 0 0"> + <TabControl.Resources> + <Style TargetType="TabPanel"> + <Setter Property="HorizontalAlignment" Value="Center"/> + </Style> + <Style TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}"> + <Setter Property="Padding" Value="20,2"></Setter> + </Style> + </TabControl.Resources> + <TabItem Header="STATS" Margin="-100 0 0 0 "> + <local:ChartsView x:Name="processParametersView" DataContext="{Binding ChartsViewVM}"/> + </TabItem> + <TabItem Header="JOB RUNS" Margin="-100 0 0 0 "> + <local:JobRunsView x:Name="jobRunsView" DataContext="{Binding JobRunsViewVM}"/> + </TabItem> + </TabControl> + </Grid> </Grid> </UserControl> diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/MainView.xaml.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/MainView.xaml.cs index 3948c4e5a..f0d1c39a7 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/MainView.xaml.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/MainView.xaml.cs @@ -28,10 +28,6 @@ namespace Tango.MachineStudio.Statistics.Views InitializeComponent(); } - private void PieChart_DataHover(object sender, ChartPoint chartPoint) - { - var tooltip = ((chartPoint.ChartView as PieChart).DataTooltip as Tooltips.PieChartTooltipControl).Title; - txtPieTitle.Text = tooltip; - } + } } |
