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.PPC.Common; using Tango.PPC.Jobs.Messages; using System.Data.Entity; using Tango.Core.Commands; using System.Windows; using Tango.Touch.Controls; using System.Windows.Media; using Tango.DragAndDrop; using System.ComponentModel; using System.Windows.Data; using Tango.PPC.Jobs.Dialogs; using Tango.PPC.Jobs.Views; using System.Runtime.InteropServices; using System.Threading; using Tango.SharedUI.Helpers; using Tango.PPC.Common.Navigation; using Tango.PPC.Jobs.NavigationObjects; using Tango.PPC.Jobs.ViewContracts; using System.Collections.ObjectModel; using Tango.PPC.Common.Models; using Tango.Logging; using Tango.PPC.Common.Messages; using Tango.BL.Builders; using Tango.PPC.Jobs.AppButtons; using Tango.Core.Threading; using System.Diagnostics; using System.Runtime.ExceptionServices; using Tango.Explorer; using Tango.PPC.Storage; using System.IO; using Tango.ColorConversion; using Tango.Integration.Operation; using Tango.BL.Enumerations; using Tango.PPC.Common.Lubrication; namespace Tango.PPC.Jobs.ViewModels { /// /// Represents the selected job view model. /// /// public class JobViewVM : PPCViewModel, INavigationObjectReceiver { private ObservablesContext _db; private bool _can_navigate_back; private Thread _check_gamut_thread; private Job _job_to_load; private JobNavigationIntent _job_to_load_intent; private static Dictionary> _jobs_fine_tune_items; public static StartPrintingButton _start_printing_btn; private ActionTimer _volumeConversionTimer; private IColorConverter _converter; private string _current_job_string; private bool startingJob = false; #region Properties private Job _job; /// /// Gets or sets the selected job. /// public Job Job { get { return _job; } set { _job = value; RaisePropertyChangedAuto(); } } private ICollectionView _segmentsCollectionView; /// /// Gets or sets the job segments collection view. /// public ICollectionView SegmentsCollectionView { get { return _segmentsCollectionView; } set { _segmentsCollectionView = value; RaisePropertyChangedAuto(); } } private List _colorSpaces; /// /// Gets or sets the available color spaces. /// public List ColorSpaces { get { return _colorSpaces; } set { _colorSpaces = value; RaisePropertyChangedAuto(); } } private List _rmls; /// /// Gets or sets the available RMLS. /// public List Rmls { get { return _rmls; } set { _rmls = value; RaisePropertyChangedAuto(); } } private Rml _selectedRML; /// /// Gets or sets the selected RML. /// public Rml SelectedRML { get { return _selectedRML; } set { _selectedRML = value; RaisePropertyChangedAuto(); OnSelectedRmlChanged(); } } private List _spoolTypes; /// /// Gets or sets the available spool types. /// public List SpoolTypes { get { return _spoolTypes; } set { _spoolTypes = value; RaisePropertyChangedAuto(); } } private List _customers; /// /// Gets or sets the available customers. /// public List Customers { get { return _customers; } set { _customers = value; RaisePropertyChangedAuto(); } } private String _customersFilter; /// /// Gets or sets the customers filter. /// public String CustomersFilter { get { return _customersFilter; } set { _customersFilter = value; RaisePropertyChangedAuto(); } } /// /// Gets or sets the customers automatic complete provider. /// public AutoCompleteProvider CustomersAutoCompleteProvider { get; set; } private ObservableCollection _fineTuneItems; /// /// Gets or sets the fine tune items. /// public ObservableCollection FineTuneItems { get { return _fineTuneItems; } set { _fineTuneItems = value; RaisePropertyChangedAuto(); } } private ObservableCollection _approvalFineTuneItems; /// /// Gets or sets the fine tune items. /// public ObservableCollection ApprovalFineTuneItems { get { return _approvalFineTuneItems; } set { _approvalFineTuneItems = value; RaisePropertyChangedAuto(); } } private bool _isFineTuneExpanded; /// /// Gets or sets a value indicating whether the fine tuning region is expanded. /// public bool IsFineTuneExpanded { get { return _isFineTuneExpanded; } set { _isFineTuneExpanded = value; RaisePropertyChangedAuto(); if (_isFineTuneExpanded) { SyncFineTuneItemsToBrushStops(); } } } private bool _isJobDetailsExpanded; /// /// Gets or sets a value indicating whether the job details area is expanded. /// public bool IsJobDetailsExpanded { get { return _isJobDetailsExpanded; } set { _isJobDetailsExpanded = value; RaisePropertyChangedAuto(); } } /// /// Gets or sets the twine catalog automatic complete provider. /// public IAutoCompleteProvider CatalogAutoCompleteProvider { get; set; } private List _availableCatalogs; public List AvailableCatalogs { get { return _availableCatalogs; } set { _availableCatalogs = value; RaisePropertyChangedAuto(); } } private RmlLubricationLevel _lubricationLevel; public RmlLubricationLevel LubricationLevel { get { return _lubricationLevel; } set { _lubricationLevel = value; RaisePropertyChangedAuto(); } } private int _btsrSpoolTension; public int BtsrSpoolTension { get { return _btsrSpoolTension; } set { _btsrSpoolTension = value; RaisePropertyChangedAuto(); } } #endregion #region Commands /// /// Gets or sets the add solid segment command. /// public RelayCommand AddSolidSegmentCommand { get; set; } /// /// Gets or sets the add gradient segment command. /// public RelayCommand AddGradientSegmentCommand { get; set; } /// /// Gets or sets the add brush stop command. /// public RelayCommand AddBrushStopCommand { get; set; } /// /// Gets or sets the segment dropped command. /// public RelayCommand SegmentDroppedCommand { get; set; } /// /// Gets or sets the remove segment command. /// public RelayCommand RemoveSegmentCommand { get; set; } /// /// Gets or sets the remove brush stop command. /// public RelayCommand RemoveBrushStopCommand { get; set; } /// /// Gets or sets the remove job command. /// public RelayCommand RemoveJobCommand { get; set; } /// /// Gets or sets the save job command. /// public RelayCommand SaveJobCommand { get; set; } /// /// Gets or sets the replace brush stop command. /// public RelayCommand ReplaceBrushStopCommand { get; set; } /// /// Gets or sets the twine catalog field tap command. /// public RelayCommand OpenCatalogCommand { get; set; } /// /// Gets or sets the increase decrease samples to dye command. /// public RelayCommand IncreaseDecreaseSamplesToDyeCommand { get; set; } /// /// Gets or sets the start sample dye command. /// public RelayCommand StartSampleDyeCommand { get; set; } /// /// Gets or sets the dye command. /// public RelayCommand DyeCommand { get; set; } /// /// Gets or sets the approve sample command. /// public RelayCommand ApproveSampleCommand { get; set; } /// /// Gets or sets the repeat sample dye command. /// public RelayCommand RepeatSampleDyeCommand { get; set; } /// /// Gets or sets another sample command. /// public RelayCommand AnotherSampleCommand { get; set; } /// /// Gets or sets the invoke fine tuning palette command. /// public RelayCommand InvokeFineTuningPaletteCommand { get; set; } /// /// Gets or sets the reset fine tuning command. /// public RelayCommand ResetFineTuningCommand { get; set; } /// /// Gets or sets the start fine tuning command. /// public RelayCommand StartFineTuningCommand { get; set; } /// /// Gets or sets the approve fine tuning command. /// public RelayCommand ApproveFineTuningCommand { get; set; } /// /// Gets or sets the repeat fine tuning command. /// public RelayCommand RepeatFineTuningCommand { get; set; } /// /// Gets or sets the export embroidery command. /// public RelayCommand ExportEmbroideryCommand { get; set; } #endregion #region Constructors /// /// Initializes the class. /// static JobViewVM() { _jobs_fine_tune_items = new Dictionary>(); } /// /// Initializes a new instance of the class. /// public JobViewVM() { _converter = new DefaultColorConverter(); _volumeConversionTimer = new ActionTimer(TimeSpan.FromMilliseconds(50)); RegisterForMessage(HandleJobSelectedMessage); FineTuneItems = new ObservableCollection(); ApprovalFineTuneItems = new ObservableCollection(); CustomersAutoCompleteProvider = new AutoCompleteProvider((customer, filter) => { return customer.Name.ToLower().StartsWith(filter != null ? filter.ToLower() : String.Empty); }); CatalogAutoCompleteProvider = new AutoCompleteProvider((item, filter) => { return !String.IsNullOrWhiteSpace(filter) && item.Name.ToLower().StartsWith(filter.ToLower()); }); //Initialize Commands AddSolidSegmentCommand = new RelayCommand(() => AddSolidSegment()); AddBrushStopCommand = new RelayCommand(AddBrushStop); AddGradientSegmentCommand = new RelayCommand(() => AddGradientSegment()); SegmentDroppedCommand = new RelayCommand((e) => { DragAndDropSegment( (e.Draggable as FrameworkElement).DataContext as Segment, (e.Droppable as FrameworkElement).DataContext as Segment); }); RemoveSegmentCommand = new RelayCommand(RemoveSegment); RemoveBrushStopCommand = new RelayCommand(RemoveBrushStop); RemoveJobCommand = new RelayCommand(RemoveJob); SaveJobCommand = new RelayCommand(() => SaveJob()); ReplaceBrushStopCommand = new RelayCommand(InvokeColorAdjustmentForBrushStop); IncreaseDecreaseSamplesToDyeCommand = new RelayCommand((x) => { if (x == "+") { Job.SampleUnitsOrMeters++; } else { Job.SampleUnitsOrMeters--; } }); _check_gamut_thread = new Thread(CheckGamutThreadMethod); _check_gamut_thread.IsBackground = true; StartSampleDyeCommand = new RelayCommand(StartSampleDye, CanStartJob); DyeCommand = new RelayCommand(StartJob, CanStartJob); ApproveSampleCommand = new RelayCommand(ApproveSampleDye); RepeatSampleDyeCommand = new RelayCommand(RepeatSampleDye); AnotherSampleCommand = new RelayCommand(DyeAnotherSample); InvokeFineTuningPaletteCommand = new RelayCommand(InvokeFineTuningPalette); ResetFineTuningCommand = new RelayCommand(() => ResetFineTuning(true)); StartFineTuningCommand = new RelayCommand(StartFineTuning, () => FineTuneItems.Any(x => x.IsSelected) && CanStartJob()); RepeatFineTuningCommand = new RelayCommand(RepeatFineTuning); ApproveFineTuningCommand = new RelayCommand(ApproveFineTuning); OpenCatalogCommand = new RelayCommand(OpenCatalog); ExportEmbroideryCommand = new RelayCommand(ExportEmbroidery); } #endregion #region Job Management /// /// Loads the job. /// private async void LoadJob() { try { if (!(_job_to_load == null || (_job_to_load != null && Job != null && _job_to_load.Guid == Job.Guid))) { View.ScrollToTop(); LogManager.Log($"Loading selected job '{_job_to_load.Name}'..."); //NotificationProvider.SetGlobalBusyMessage("Loading job details..."); IsFree = false; _can_navigate_back = false; if (_db != null) { if (Job != null) { //Job.RmlChanged -= OnRmlChanged; Job.NameChanged -= Job_NameChanged; foreach (var stop in Job.Segments.SelectMany(x => x.BrushStops).ToList()) { stop.ColorSpaceChanged -= Stop_ColorSpaceChanged; stop.ColorCatalogChanged -= Stop_ColorCatalogChanged; } } if (Rmls != null) { Rmls.Where(x => x.Cct != null && x.Cct.Data != null).ToList().ForEach(x => x.Cct.Data = null); Rmls.ForEach(x => x.Cct = null); if (SelectedRML != null) { SelectedRML.Cct = null; SelectedRML = null; } Rmls = null; } _db.Dispose(); GC.Collect(); } _db = ObservablesContext.CreateDefault(); Job = await new JobBuilder(_db).Set(_job_to_load.Guid) .WithConfiguration() .WithUser() .WithSegments() .WithBrushStops() .BuildAsync(); //Job.RmlChanged -= OnRmlChanged; //Job.RmlChanged += OnRmlChanged; Job.NameChanged -= Job_NameChanged; Job.NameChanged += Job_NameChanged; Job.ValidateOnPropertyChanged = true; //GetLubricationLevel(); //await SetSpoolTension(Job.Rml); LogManager.Log("Loading RMLS..."); Rmls = (await new RmlsCollectionBuilder(_db).SetAll().ForHeadType(MachineProvider.Machine.MachineHeadType).ForSite(MachineProvider.Machine.SiteGuid).BuildAsync()).OrderBy(x => x.FinalName).ToList(); LogManager.Log("Loading Color Spaces..."); ColorSpaces = await _db.ColorSpaces.Where(x => x.Code != (int)BL.Enumerations.ColorSpaces.CMYK).ToListAsync(); LogManager.Log("Loading Spool Types..."); SpoolTypes = await _db.SpoolTypes.ToListAsync(); LogManager.Log("Loading Customers..."); Customers = await _db.Customers.Where(x => x.OrganizationGuid == MachineProvider.Machine.OrganizationGuid).ToListAsync(); AvailableCatalogs = await new CatalogsCollectionBuilder(_db).SetAll().WithGroups().WithItems().ForSite(MachineProvider.Machine.SiteGuid).BuildListAsync(); _selectedRML = Job.Rml; RaisePropertyChanged(nameof(SelectedRML)); await LoadRML(_selectedRML); if (!_check_gamut_thread.IsAlive) { _check_gamut_thread.Start(); } SegmentsCollectionView = CollectionViewSource.GetDefaultView(Job.Segments); SegmentsCollectionView.SortDescriptions.Add(new SortDescription(nameof(Segment.SegmentIndex), ListSortDirection.Ascending)); //ResetFineTuning(); _job_to_load = null; _current_job_string = Job.ToJobFileWhenLoaded().ToString(); } if (!_jobs_fine_tune_items.ContainsKey(Job.Guid) && Job.JobFineTuningStatus == BL.Enumerations.FineTuningStatuses.PendingApproval) { Job.JobFineTuningStatus = BL.Enumerations.FineTuningStatuses.Unspecified; } if (_job_to_load_intent == JobNavigationIntent.NewJob) { IsJobDetailsExpanded = true; } LogManager.Log($"Job editing state = '{Job.JobEditingState}'."); if (Job.JobEditingState == BL.Enumerations.EditingStates.SampleDye && Job.JobSampleDyeStatus == BL.Enumerations.SampleDyeStatuses.PendingApproval) { LogManager.Log("Directing view to display sample dye region."); View.DisplaySampleDye(); } else if (Job.JobEditingState == BL.Enumerations.EditingStates.FineTuning && Job.JobFineTuningStatus == BL.Enumerations.FineTuningStatuses.PendingApproval) { LogManager.Log("Directing view to display fine tuning region."); View.DisplayFineTuning(); } ValidateBrushStops(); RegisterJobBrushStopsEvents(); DyeCommand.RaiseCanExecuteChanged(); StartSampleDyeCommand.RaiseCanExecuteChanged(); StartFineTuningCommand.RaiseCanExecuteChanged(); } catch (Exception ex) { IsFree = true; LogManager.Log(ex, $"Error loading job '{(_job_to_load != null ? _job_to_load.Name : "null")}'"); await NotificationProvider.ShowError("An error occurred while trying to load the selected job."); _can_navigate_back = true; await NavigationManager.NavigateBack(); } finally { IsFree = true; } } private void Job_NameChanged(object sender, string e) { DyeCommand.RaiseCanExecuteChanged(); } /// /// Saves the job. /// private async void SaveJob(bool displayNotification = true) { try { if (Job.Validate(_db)) { LogManager.Log("Saving job..."); if (!String.IsNullOrWhiteSpace(CustomersFilter)) { if (!Customers.Exists(x => x.Name == CustomersFilter)) { var newCustomer = new Customer() { OrganizationGuid = MachineProvider.Machine.OrganizationGuid, Name = CustomersFilter, }; _db.Customers.Add(newCustomer); Job.Customer = newCustomer; } } Job.LastUpdated = DateTime.UtcNow; Job.Version = 1; Job.IsSynchronized = false; Job.JobStatus = BL.Enumerations.JobStatuses.Draft; Job.MarkModified(_db); await _db.SaveChangesAsync(); _current_job_string = Job.ToJobFileWhenLoaded().ToString(); if (displayNotification) { await NotificationProvider.ShowInfo(String.Format("Job '{0}' saved successfully.", Job.Name)); } RaiseMessage(new JobSavedMessage() { Job = Job }); } else { await NotificationProvider.ShowError($"Error saving job. {Job.ValidationErrors.FirstOrDefault()}"); } } catch (Exception ex) { LogManager.Log(ex, $"Error saving job '{Job.Name}'."); await NotificationProvider.ShowError("An error occurred while trying to save the job."); } } /// /// Removes the job. /// private async void RemoveJob() { try { LogManager.Log("Removing job..."); if (await NotificationProvider.ShowQuestion("Are you sure you want to delete the this job?")) { await Job.DeleteCascadeAsync(_db); RaiseMessage(new JobRemovedMessage() { Job = Job }); _can_navigate_back = true; await NavigationManager.NavigateBack(); } } catch (Exception ex) { LogManager.Log(ex, $"Error removing job '{Job.Name}'."); await NotificationProvider.ShowError("An error occurred while trying to remove the job."); } } /// /// Starts the job. /// private async void StartJob() { if (startingJob) return; try { Debug.WriteLine("Job Starting..."); startingJob = true; LogManager.Log("Start job command pressed. Starting job and navigating to job progress view..."); var handler = await PrintingManager.Print(Job, _db); await NavigationManager.NavigateTo(nameof(JobProgressView)); startingJob = false; } catch (InsufficientLiquidQuantityException) { //Ignore.. } catch (OperationCanceledException) { //Ignore.. } catch (Exception ex) { LogManager.Log(ex, "Could not start the current job."); await NotificationProvider.ShowError($"{ex.Message}."); } finally { startingJob = false; } } /// /// Determines whether this instance [can start job]. /// private bool CanStartJob() { try { return Job != null && Job.Validate(_db) && !Job.Segments.SelectMany(x => x.BrushStops).Where(x => !x.IsTransparent && !x.IsWhite).ToList().Exists(x => x.IsOutOfGamut || (x.BrushColorSpace == BL.Enumerations.ColorSpaces.Volume && x.IsLiquidVolumesOutOfRange)); } catch (Exception ex) { Debug.WriteLine(ex); return false; } } #endregion #region RML Changed private async void OnSelectedRmlChanged() { await LoadRML(SelectedRML); } private async Task LoadRML(Rml rml) { if (rml != null) { if (Job.Rml != rml || rml.Cct == null) { bool updateVolumes = Job.Rml != rml; Job.Rml = await new RmlBuilder(_db) .Set(rml.Guid) .WithActiveParametersGroup() .WithCCT() .WithGbdAndLub() .WithCAT(MachineProvider.Machine.Guid) .WithLiquidFactors() .WithSpools() .BuildAsync(); foreach (var segment in Job.Segments) { SetSegmentLiquidVolumes(segment); } GetLubricationLevel(); await SetSpoolTension(rml); if (updateVolumes) { NotificationProvider.SetGlobalBusyMessage("Updating job liquid volumes..."); foreach (var stop in Job.Segments.SelectMany(x => x.BrushStops).Where(x => x.BrushColorSpace == BL.Enumerations.ColorSpaces.RGB || x.BrushColorSpace == BL.Enumerations.ColorSpaces.LAB).ToList()) { try { var output = await _converter.ConvertAsync(stop, false, false); output.ApplyOnBrushStopVolumesOnly(stop); } catch (Exception ex) { LogManager.Log(ex, $"Error updating stop volumes after changing thread on segment {stop.Segment.SegmentIndex}, stop {stop.StopIndex}."); } } NotificationProvider.ReleaseGlobalBusyMessage(); } } } } #endregion #region Segments Management /// /// Adds a new solid segment. /// private Segment AddSolidSegment() { try { LogManager.Log("Adding new solid segment..."); var s = Job.AddSolidSegment(Settings.DefaultSegmentLength > 0 ? Settings.DefaultSegmentLength : 10); SetSegmentLiquidVolumes(s); RegisterJobBrushStopsEvents(); return s; } catch (Exception ex) { LogManager.Log(ex, "Could not add a new solid segment."); NotificationProvider.ShowError("An error occurred while trying to add a new segment."); return null; } } /// /// Adds a new gradient segment. /// private Segment AddGradientSegment() { try { LogManager.Log("Adding new gradient segment..."); var s = Job.AddGradientSegment(Settings.DefaultSegmentLength > 0 ? Settings.DefaultSegmentLength : 10); SetSegmentLiquidVolumes(s); RegisterJobBrushStopsEvents(); return s; } catch (Exception ex) { LogManager.Log(ex, "Could not add a new gradient segment."); NotificationProvider.ShowError("An error occurred while trying to add a new segment."); return null; } } /// /// Called when a segment has been dragged and dropped into another segment. /// /// The dragged job. /// The dropped job. private void DragAndDropSegment(Segment draggedSegment, Segment droppedSegment) { LogManager.Log($"Segment Drag & Drop '{draggedSegment.SegmentIndex}' => '{droppedSegment.SegmentIndex}'."); if (draggedSegment.SegmentIndex > droppedSegment.SegmentIndex) { draggedSegment.SegmentIndex = droppedSegment.SegmentIndex - 1; } else { draggedSegment.SegmentIndex = droppedSegment.SegmentIndex + 1; } ArrangeSegmentsIndices(); } /// /// Removes the segment. /// /// The segment. private async void RemoveSegment(Segment segment) { if (Job.Segments.Count > 1) { try { if (await NotificationProvider.ShowQuestion("Are you sure you want to remove the selected segment?")) { LogManager.Log($"Removing job segment {segment.SegmentIndex}"); segment.BrushStops.ToList().ForEach(x => { x.ColorSpaceChanged -= Stop_ColorSpaceChanged; _db.BrushStops.Remove(x); }); _db.Segments.Remove(segment); ArrangeSegmentsIndices(); DyeCommand.RaiseCanExecuteChanged(); } } catch (Exception ex) { LogManager.Log(ex, "Could not remove segment."); await NotificationProvider.ShowError("An error occurred while trying to add a new segment."); } } else { await NotificationProvider.ShowInfo("A job must contain at least one color segment."); } } /// /// Sets the segment liquid volumes. /// /// The segment. private void SetSegmentLiquidVolumes(Segment segment) { foreach (var stop in segment.BrushStops) { stop.SetLiquidVolumes(Job.Machine.Configuration, Job.Rml, Job.Rml.GetActiveProcessGroup().ProcessParametersTables.FirstOrDefault()); var lub = stop.LiquidVolumes.FirstOrDefault(x => x.IdsPack.LiquidType.Code == (int)BL.Enumerations.LiquidTypes.Lubricant); if (lub != null) { lub.Volume = 100; } } } private void ArrangeSegmentsIndices() { int index = 1; foreach (var segment in Job.Segments.OrderBy(x => x.SegmentIndex)) { segment.SegmentIndex = index++; } SegmentsCollectionView.Refresh(); } #endregion #region Brush Stops Management private void RegisterJobBrushStopsEvents() { if (Job != null) { foreach (var stop in Job.Segments.SelectMany(x => x.BrushStops).ToList()) { stop.ColorSpaceChanged -= Stop_ColorSpaceChanged; stop.ColorSpaceChanged += Stop_ColorSpaceChanged; stop.ColorCatalogChanged -= Stop_ColorCatalogChanged; stop.ColorCatalogChanged += Stop_ColorCatalogChanged; } } } private void Stop_ColorSpaceChanged(object sender, ColorSpace colorSpace) { BrushStop stop = sender as BrushStop; stop.Segment.BrushStops.Where(x => x != stop).ToList().ForEach(x => x.ColorSpace = stop.ColorSpace); DyeCommand.RaiseCanExecuteChanged(); } private void Stop_ColorCatalogChanged(object sender, ColorCatalog catalog) { BrushStop stop = sender as BrushStop; if (stop.ColorSpace != null && stop.BrushColorSpace == BL.Enumerations.ColorSpaces.Catalog) { if (stop.ColorCatalogsItem != null) { try { if (catalog != null && catalog.AllItemsOrdered.Count > 0) { stop.ColorCatalogsItem = catalog.GetClosestItem(stop.ColorCatalogsItem.Color); } else { stop.ColorCatalogsItem = null; } } catch (Exception ex) { LogManager.Log(ex, "Error getting closest catalog color."); stop.ColorCatalogsItem = null; } } } } /// /// Adds a new brush stop to the specified segment. /// /// The segment. private void AddBrushStop(Segment segment) { LogManager.Log($"Adding new brush stop to segment {segment.SegmentIndex}."); segment.AddBrushStop(); SetSegmentLiquidVolumes(segment); RegisterJobBrushStopsEvents(); } /// /// Removes the brush stop. /// /// The brush stop. private void RemoveBrushStop(BrushStop brushStop) { if (brushStop.Segment.BrushStops.Count > 2) { LogManager.Log($"removing brush stop {brushStop.StopIndex} from segment {brushStop.Segment.SegmentIndex}."); var segment = brushStop.Segment; brushStop.ColorSpaceChanged -= Stop_ColorSpaceChanged; _db.BrushStops.Remove(brushStop); ArrangeBrushStopsIndices(segment); } else { NotificationProvider.ShowInfo("Gradient segments must contain at least two colors."); } } /// /// Invokes the color adjustment for the specified brush stop. /// /// The brush stop. private async void InvokeColorAdjustmentForBrushStop(BrushStop brushStop) { try { LogManager.Log($"Invoking triplet color adjustment dialog for brush stop {brushStop.StopIndex} at segment {brushStop.Segment.SegmentIndex}."); LogManager.Log("Retrieving color conversion suggestions for brush stop..."); PMR.ColorLab.ConversionOutput conversionOutput = null; if (brushStop.IsOutOfGamut) { conversionOutput = _converter.Convert(brushStop, false); } BasicColorCorrectionViewVM vm = null; List suggestions = null; if (brushStop.IsOutOfGamut) { vm = await NotificationProvider.ShowDialog(new BasicColorCorrectionViewVM() { InvalidBrushStop = brushStop, Suggestions = new List() { new ColorConversionSuggestion(conversionOutput.SingleCoordinates, 0, 0) }, }); } if (vm == null || vm.Result == BasicColorCorrectionViewVM.ColorCorrectionDialogResult.MoreOptions) { NotificationProvider.SetGlobalBusyMessage("Generating color hive..."); await Task.Factory.StartNew(() => { conversionOutput = _converter.Convert(brushStop, true); suggestions = conversionOutput.CreateHiveSuggestions(); if (vm == null) { var center = suggestions.GetCenterSuggestion(); center.Coordinates.Red = brushStop.Red; center.Coordinates.Green = brushStop.Green; center.Coordinates.Blue = brushStop.Blue; center.Coordinates.L = brushStop.L; center.Coordinates.A = brushStop.A; center.Coordinates.B = brushStop.B; } }); NotificationProvider.ReleaseGlobalBusyMessage(); LogManager.Log("Invoking hive color conversion dialog..."); vm = await NotificationProvider.ShowDialog(new AdvancedColorCorrectionViewVM() { InvalidBrushStop = brushStop, Suggestions = suggestions, IsOutOfGamut = brushStop.IsOutOfGamut, }); } if (vm.Result == BasicColorCorrectionViewVM.ColorCorrectionDialogResult.Confirmed) { LogManager.Log($"Color suggestion selected: {vm.SelectedSuggestion.Color.ToString()}."); if (brushStop.BrushColorSpace == BL.Enumerations.ColorSpaces.RGB) { brushStop.Red = vm.SelectedSuggestion.Coordinates.Red; brushStop.Green = vm.SelectedSuggestion.Coordinates.Green; brushStop.Blue = vm.SelectedSuggestion.Coordinates.Blue; } else if (brushStop.BrushColorSpace == BL.Enumerations.ColorSpaces.LAB) { brushStop.L = vm.SelectedSuggestion.Coordinates.L; brushStop.A = vm.SelectedSuggestion.Coordinates.A; brushStop.B = vm.SelectedSuggestion.Coordinates.B; } else if (brushStop.BrushColorSpace == BL.Enumerations.ColorSpaces.Volume) { vm.SelectedSuggestion.ApplyOnBrushStop(brushStop); } brushStop.Corrected = true; brushStop.IsOutOfGamut = false; brushStop.OutOfGamutChecked = true; } } catch (Exception ex) { LogManager.Log(ex, "Error while invoking color adjustment dialog."); await NotificationProvider.ShowError("An error occurred while trying to convert the selected color."); } finally { NotificationProvider.ReleaseGlobalBusyMessage(); DyeCommand.RaiseCanExecuteChanged(); } } /// /// Called when the brush stop field value has been changed (This called from the view!). /// /// The brush stop. [HandleProcessCorruptedStateExceptions] public void OnBrushStopFieldValueChanged(BrushStop stop) { if (stop != null && stop.ColorSpace != null) { if (stop.BrushColorSpace == BL.Enumerations.ColorSpaces.Catalog) { DyeCommand.RaiseCanExecuteChanged(); return; } _volumeConversionTimer.ResetReplace(() => { try { var output = _converter.Convert(stop, false); if (stop.BrushColorSpace == BL.Enumerations.ColorSpaces.Volume) { stop.Red = output.SingleCoordinates.Red; stop.Green = output.SingleCoordinates.Green; stop.Blue = output.SingleCoordinates.Blue; stop.L = output.SingleCoordinates.L; stop.A = output.SingleCoordinates.A; stop.B = output.SingleCoordinates.B; stop.Corrected = false; stop.OutOfGamutChecked = false; } else if (stop.BrushColorSpace == BL.Enumerations.ColorSpaces.LAB) { output.ApplyOnBrushStopVolumesOnly(stop); stop.Corrected = false; stop.OutOfGamutChecked = false; } else if (stop.BrushColorSpace == BL.Enumerations.ColorSpaces.RGB) { output.ApplyOnBrushStopVolumesOnly(stop); stop.Corrected = false; stop.OutOfGamutChecked = false; } try { var closestItem = AvailableCatalogs.SelectMany(x => x.AllItemsOrdered).GetClosestItem(stop.Color); stop.ColorCatalog = closestItem.ColorCatalogsGroup.ColorCatalog; stop.ColorCatalogsItem = closestItem; } catch { } } catch (Exception ex) { LogManager.Log(ex, "An error occurred while trying to get volume => RGB from conversion engine."); } finally { InvokeUI(() => DyeCommand.RaiseCanExecuteChanged()); } }); } } /// /// Opens the twine catalog for the specified brush stop. /// /// The stop. private async void OpenCatalog(BrushStop stop) { if (stop.ColorCatalog == null) { await NotificationProvider.ShowInfo("Please select a color catalog first."); return; } var catalogItem = await NavigationManager.NavigateForResult(new TwineCatalogNavigationObject() { SelectedItem = stop.ColorCatalogsItem, Catalog = stop.ColorCatalog }, true); if (catalogItem != null) { stop.ColorCatalogsItem = catalogItem; } } private void ArrangeBrushStopsIndices(Segment segment) { for (int i = 0; i < segment.BrushStops.Count; i++) { segment.BrushStops[i].StopIndex = i + 1; } } private bool ValidateBrushStops() { return Job.Segments.SelectMany(x => x.BrushStops).ToList().All(x => x.Validate(_db)); } #endregion #region Job Selection Message /// /// Handles the job selected message. /// /// The message. private void HandleJobSelectedMessage(JobSelectedMessage message) { _job_to_load = message.Job; } #endregion #region Sample Dye /// /// Starts a sample dye. /// private async void StartSampleDye() { try { LogManager.Log("Sample dye command pressed..."); await PrintingManager.PrintSample(Job, _db); await NavigationManager.NavigateTo(nameof(JobProgressView)); } catch (Exception ex) { LogManager.Log(ex, $"Error executing sample dye for job {Job.Name}."); await NotificationProvider.ShowError(ex.Message); } } /// /// Sets the job status back to not approved. /// private void RepeatSampleDye() { LogManager.Log("Repeat sample dye command pressed..."); Job.JobEditingState = BL.Enumerations.EditingStates.Default; Job.JobSampleDyeStatus = BL.Enumerations.SampleDyeStatuses.Unspecified; } /// /// Approves the sample dye. /// private void ApproveSampleDye() { LogManager.Log("Approve sample dye command pressed..."); Job.JobEditingState = BL.Enumerations.EditingStates.Default; Job.JobSampleDyeStatus = BL.Enumerations.SampleDyeStatuses.Approved; Job.SampleDyeApproveDate = DateTime.UtcNow; SaveJob(false); } /// /// Dyes another sample. /// private void DyeAnotherSample() { LogManager.Log("Dye another sample dye command pressed..."); Job.JobEditingState = BL.Enumerations.EditingStates.Default; Job.JobSampleDyeStatus = BL.Enumerations.SampleDyeStatuses.Unspecified; } #endregion #region Fine Tuning /// /// Synchronizes the fine tune items to brush stops. /// private async void SyncFineTuneItemsToBrushStops(bool displayBusy = false) { try { if (Job != null) { if (_jobs_fine_tune_items.ContainsKey(Job.Guid)) { FineTuneItems = _jobs_fine_tune_items[Job.Guid].ToObservableCollection(); } else { if (displayBusy) { NotificationProvider.SetGlobalBusyMessage("Generating suggestions..."); } FineTuneItems.Clear(); foreach (var stop in Job.Segments.SelectMany(x => x.BrushStops).Where(x => !x.IsTransparent).Where(x => x.ColorSpace.Space == BL.Enumerations.ColorSpaces.RGB || x.ColorSpace.Space == BL.Enumerations.ColorSpaces.LAB).DistinctBy(x => x.Color)) { var conversionoutput = await _converter.ConvertAsync(stop, true); FineTuneItem item = new FineTuneItem(conversionoutput); item.BrushStop = stop; item.BrushStops = Job.Segments.SelectMany(x => x.BrushStops).Where(x => x.Color == stop.Color).ToList(); item.SelectedSuggestion = item.Suggestions.GetCenterSuggestion(); item.SelectedChanged += () => StartFineTuningCommand.RaiseCanExecuteChanged(); FineTuneItems.Add(item); } _jobs_fine_tune_items[Job.Guid] = FineTuneItems.ToList(); NotificationProvider.ReleaseGlobalBusyMessage(); } ApprovalFineTuneItems = FineTuneItems.Where(x => x.IsSelected).ToObservableCollection(); StartFineTuningCommand.RaiseCanExecuteChanged(); } } catch (Exception ex) { LogManager.Log(ex, "Error while trying to synchronize fine tuning items with brush stops."); } finally { NotificationProvider.ReleaseGlobalBusyMessage(); } } /// /// Invokes the fine tuning palette dialog. /// /// The fine tune item. private async void InvokeFineTuningPalette(FineTuneItem fineTuneItem) { LogManager.Log("Invoke fine tuning palette command pressed..."); try { FineTuningPaletteViewVM vm = new FineTuningPaletteViewVM(fineTuneItem, Job); await NotificationProvider.ShowDialog(vm); if (vm.DialogResult) { fineTuneItem.Suggestions = vm.Suggestions; fineTuneItem.SelectedSuggestion = vm.SelectedSuggestion; } } catch (Exception ex) { LogManager.Log(ex, "Error invoking the fine tunning palette"); await NotificationProvider.ShowError("An error occurred while trying to display the fine tunning palette."); } finally { NotificationProvider.ReleaseGlobalBusyMessage(); } } /// /// Resets the fine tuning. /// private void ResetFineTuning(bool displayBusy = false) { if (Job != null && _jobs_fine_tune_items.ContainsKey(Job.Guid)) { _jobs_fine_tune_items.Remove(Job.Guid); } SyncFineTuneItemsToBrushStops(displayBusy); } /// /// Starts the fine tuning. /// private async void StartFineTuning() { try { LogManager.Log("Start fine tunning job command pressed..."); _jobs_fine_tune_items[Job.Guid] = FineTuneItems.ToList(); await PrintingManager.PrintFineTuning(Job, _db, FineTuneItems); await NavigationManager.NavigateTo(nameof(JobProgressView)); } catch (Exception ex) { LogManager.Log(ex, "Error executing fine tuning job."); await NotificationProvider.ShowError(ex.Message); } } /// /// Approves the fine tuning. /// private void ApproveFineTuning() { LogManager.Log("Approve fine tuning command pressed."); Job.JobEditingState = BL.Enumerations.EditingStates.Default; Job.JobFineTuningStatus = BL.Enumerations.FineTuningStatuses.Approved; foreach (var item in ApprovalFineTuneItems) { foreach (var stop in item.BrushStops) { stop.Color = item.SelectedSuggestion.Color; } } Job.FineTuningApproveDate = DateTime.UtcNow; SaveJob(false); if (_jobs_fine_tune_items.ContainsKey(Job.Guid)) { _jobs_fine_tune_items.Remove(Job.Guid); } SyncFineTuneItemsToBrushStops(); } /// /// Repeats the fine tuning. /// private void RepeatFineTuning() { LogManager.Log("Repeat fine tuning command pressed."); Job.JobEditingState = BL.Enumerations.EditingStates.Default; Job.JobFineTuningStatus = BL.Enumerations.FineTuningStatuses.Unspecified; } #endregion #region Out Of Gamut Check Thread /// /// Iterates over all brush stops and checks for out of gamut. /// [HandleProcessCorruptedStateExceptions] private void CheckGamutThreadMethod() { while (true) { Thread.Sleep(500); if (Job != null && Job.Rml.Cct != null && IsVisible) { var brushStops = Job.Segments.SelectMany(x => x.BrushStops).Where(x => x.ColorSpace != null).Where(x => (x.BrushColorSpace == BL.Enumerations.ColorSpaces.LAB || x.BrushColorSpace == BL.Enumerations.ColorSpaces.RGB) && !x.Corrected && !x.OutOfGamutChecked).ToList(); foreach (var stop in brushStops) { try { stop.IsOutOfGamut = _converter.IsOutOfGamut(stop); stop.OutOfGamutChecked = true; } catch (AccessViolationException) { LogManager.Log($"Out of gamut check failed for brush stop {stop.StopIndex} at segment {stop.Segment.SegmentIndex}.", LogCategory.Warning); continue; } catch { LogManager.Log($"Out of gamut check failed for brush stop {stop.StopIndex} at segment {stop.Segment.SegmentIndex}.", LogCategory.Warning); } } if (brushStops.Count > 0) { InvokeUI(() => { DyeCommand.RaiseCanExecuteChanged(); StartSampleDyeCommand.RaiseCanExecuteChanged(); StartFineTuningCommand.RaiseCanExecuteChanged(); }); } } } } #endregion #region Lubrication Level private void GetLubricationLevel() { if (Job != null) { LubricationLevel = Settings.LubricationLevels.FirstOrDefault(x => x.RmlGuid == Job.RmlGuid); } else { LubricationLevel = null; } } #endregion #region Spool Tension private async Task SetSpoolTension(Rml rml) { if (rml != null && _db != null) { BtsrSpoolTension = await rml.GetRequiredBtsrSpoolTension(_db, Job.SpoolType); } } #endregion #region Export Embroidery private async void ExportEmbroidery() { try { if (!StorageProvider.IsConnected) { await NotificationProvider.ShowError("No storage device connected."); return; } var result = await NavigationManager. NavigateForResult( new Storage.Models.StorageNavigationRequest() { Intent = Storage.Models.StorageNavigationIntent.SaveFile, DefaultFileName = Job.Name + Path.GetExtension(Job.EmbroideryFileName), Filter = Path.GetExtension(Job.EmbroideryFileName), Title = "Export Embroidery File", }); if (result != null) { File.WriteAllBytes(Path.HasExtension(result.Path) ? result.Path : result.Path + Path.GetExtension(Job.EmbroideryFileName), Job.EmbroideryFileData); await NotificationProvider.ShowSuccess("Embroidery file exported successfully."); } } catch (Exception ex) { LogManager.Log(ex, "Error exporting embroidery file."); await NotificationProvider.ShowError("An error occurred while trying to save the selected embroidery file."); } } #endregion #region IPPC ViewModel Overrides /// /// Called when the application has been started. /// public override void OnApplicationStarted() { base.OnApplicationStarted(); MachineProvider.MachineOperator.PrintingEnded += MachineOperator_PrintingEnded; } private void MachineOperator_PrintingEnded(object sender, Integration.Operation.PrintingEventArgs e) { if (IsVisible) { _start_printing_btn.Push(); } } /// /// Called when the navigation system has navigated to this VM view. /// public override void OnNavigatedTo() { if (!MachineProvider.MachineOperator.IsPrinting) { _start_printing_btn.Push(); } base.OnNavigatedTo(); LoadJob(); } /// /// Called when the navigation system has navigated from this VM view. /// public override void OnNavigatedFrom() { _start_printing_btn.Pop(); base.OnNavigatedFrom(); _job_to_load_intent = JobNavigationIntent.Default; } /// /// Called before the navigation system navigates back from this object. /// Return false to abort the navigation. /// /// public async override Task OnNavigateBackRequest() { bool result = true; if (!IsFree) return false; if (!_can_navigate_back) { bool jobChainged = false; if (Job != null) { string job_string = Job.ToJobFileWhenLoaded().ToString(); jobChainged = job_string != _current_job_string; } if (jobChainged) { if (await NotificationProvider.ShowQuestion("Are you sure you want to exit this job without saving changes?")) { Job = null; SegmentsCollectionView = null; } else { result = false; } } else { Job = null; SegmentsCollectionView = null; } } return result; } public override void OnApplicationReady() { base.OnApplicationReady(); _start_printing_btn = new StartPrintingButton(DyeCommand, MachineProvider.MachineOperator); } #endregion #region INavigationObjectReceiver public void OnNavigatedToWithObject(JobNavigationObject e) { _job_to_load_intent = e.Intent; _job_to_load = e.Job; } #endregion } }