using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Media; using Tango.Core.Commands; using Tango.BL.Entities; using Tango.Integration.Operation; using Tango.Integration.ExternalBridge; using Tango.Logging; using Tango.MachineStudio.Common.Authentication; using Tango.MachineStudio.Common.Controls; using Tango.MachineStudio.Common.Diagnostics; using Tango.MachineStudio.Common.Notifications; using Tango.MachineStudio.Common.StudioApplication; using Tango.MachineStudio.Common.Video; using Tango.MachineStudio.Developer.Navigation; using Tango.MachineStudio.Developer.Views; using Tango.Settings; using Tango.SharedUI; using Tango.Transport; using Tango.BL; using Microsoft.Win32; using Tango.PMR.Embroidery; using Tango.EmbroideryUI; using System.IO; using System.Windows; using Tango.Core.Helpers; using System.Speech.Synthesis; using System.Media; using Tango.MachineStudio.Common.EventLogging; using Tango.MachineStudio.Common.Speech; using System.Threading; using Tango.SharedUI.Helpers; using Tango.Core.DI; using Tango.MachineStudio.Common; using Tango.MachineStudio.Logging.ViewModels; using Tango.MachineStudio.Logging.Views; using Tango.AutoComplete.Editors; using System.Data.Entity; using System.Runtime.ExceptionServices; using Tango.BL.Builders; using Tango.MachineStudio.Common.Navigation; using System.Diagnostics; using Tango.Core.ExtensionMethods; using Tango.ColorConversion; using Tango.PMR.Exports; using Microsoft.WindowsAPICodePack.Dialogs; using Tango.BL.Enumerations; using Tango.BL.DTO; using Tango.BL.ActionLogs; namespace Tango.MachineStudio.Developer.ViewModels { /// /// Represents the developer module main view, view model. /// /// [TangoCreateWhenRegistered] public class MainViewVM : StudioViewModel { private static object _syncLock = new object(); private const string EMB_FORMATS_EXPORT = "Baby Lock (PES)|*.pes|Tajima (DST)|*.dst|EXP|*.exp|PCS|*.pcs|HUS|*.hus|KSM|*.ksm"; private const string EMB_FORMATS_IMPORT = "Embroidery Files|*.pes;*.hus;*.dst"; private INotificationProvider _notification; private TimeSpan _runningJobEstimatedDuration; private DeveloperNavigationManager _navigation; private INavigationManager _msNavigation; private bool _blockInvalidateCommands; private ObservablesContext _machineDbContext; private ObservablesContext _activeJobDbContext; private IEventLogger _eventLogger; private ISpeechProvider _speech; private DataCapture.ViewModels.MainViewVM _dataCaptureVM; private bool _isRecording; private DeveloperModuleSettings _settings; private Thread _colorConversionThread; private bool _hiveOpened; private bool _color_changed_from_hive; private bool _dialog_shown; private bool _disable_gamut_check; private bool _rml_has_no_cct; private TaskItem _preparingTaskItem; private IColorConverter _converter; private string _current_job_string; private JobDTO _beforeSaveJobDTO; private IActionLogManager _actionLogManager; private RmlDTO _selectedRMLBeforeLiquidFactorsSaves; private List _cctCache; #region Properties private List _hiveSuggestions; /// /// Gets or sets the hive suggestions. /// public List HiveSuggestions { get { return _hiveSuggestions; } set { _hiveSuggestions = value; RaisePropertyChangedAuto(); } } private ColorConversionSuggestion _selectedSuggestion; /// /// Gets or sets the selected suggestion. /// public ColorConversionSuggestion SelectedSuggestion { get { return _selectedSuggestion; } set { _selectedSuggestion = value; RaisePropertyChangedAuto(); OnSelectedSuggestionChanged(); } } private RunningJobStatus _runningJobStatus; /// /// Gets or sets the running job status. /// public RunningJobStatus RunningJobStatus { get { return _runningJobStatus; } set { _runningJobStatus = value; RaisePropertyChangedAuto(); } } private JobHandler _jobHandler; /// /// Gets or sets the current running job handler. /// public JobHandler JobHandler { get { return _jobHandler; } set { _jobHandler = value; RaisePropertyChangedAuto(); } } private ObservableCollection _colorSpaces; /// /// Gets or sets the color spaces. /// public ObservableCollection ColorSpaces { get { return _colorSpaces; } set { _colorSpaces = value; RaisePropertyChangedAuto(); } } private ObservableCollection _rmls; /// /// Gets or sets the RMLS. /// public ObservableCollection Rmls { get { return _rmls; } set { _rmls = value; RaisePropertyChangedAuto(); } } private ObservableCollection _windingMethods; /// /// Gets or sets the winding methods. /// public ObservableCollection WindingMethods { get { return _windingMethods; } set { _windingMethods = value; RaisePropertyChangedAuto(); } } private ObservableCollection _spoolTypes; /// /// Gets or sets the spool types. /// public ObservableCollection SpoolTypes { get { return _spoolTypes; } set { _spoolTypes = value; RaisePropertyChangedAuto(); } } /// /// Gets or sets the application manager. /// public IStudioApplicationManager ApplicationManager { get; set; } /// /// Gets or sets the video capture provider. /// public IVideoCaptureProvider VideoCaptureProvider { get; set; } protected Machine _selectedMachine; /// /// Gets or sets the selected machine. /// public Machine SelectedMachine { get { return _selectedMachine; } set { if (value != null && _selectedMachine != value) { _selectedMachine = value; OnSelectedMachineChanged(); RaisePropertyChangedAuto(); InvalidateRelayCommands(); if (_selectedMachine != null) { _selectedMachine.Modified -= SelectedMachine_Modified; _selectedMachine.Modified += SelectedMachine_Modified; } } } } private bool _canWork; /// /// Gets or sets a value indicating whether this instance is loading machine. /// public bool CanWork { get { return _canWork; } set { _canWork = value; RaisePropertyChangedAuto(); } } private List _liquidTypesRmls; /// /// Gets or sets the liquid types RMLS. /// public List LiquidTypesRmls { get { return _liquidTypesRmls; } set { _liquidTypesRmls = value; RaisePropertyChangedAuto(); } } private ProcessParametersTablesGroup _rmlProcessParametersTablesGroup; /// /// Gets or sets the RML process parameters table group (cloned). /// public ProcessParametersTablesGroup RmlProcessParametersTableGroup { get { return _rmlProcessParametersTablesGroup; } set { _rmlProcessParametersTablesGroup = value; RaisePropertyChangedAuto(); OnProcessParametersTableGroupChanged(); } } private ObservableCollection _groupsHistory; /// /// Gets or sets the RML process parameters groups history. /// public ObservableCollection GroupsHistory { get { return _groupsHistory; } set { _groupsHistory = value; RaisePropertyChangedAuto(); } } private ProcessParametersTablesGroup _selectedGroupHistory; /// /// Gets or sets the selected process parameters tables group history. /// public ProcessParametersTablesGroup SelectedGroupHistory { get { return _selectedGroupHistory; } set { _selectedGroupHistory = value; RaisePropertyChangedAuto(); OnSelectedGroupHistoryChanged(); } } private ProcessParametersTable _selectedProcessParametersTable; /// /// Gets or sets the selected process parameters table. /// public ProcessParametersTable SelectedProcessParametersTable { get { return _selectedProcessParametersTable; } set { _selectedProcessParametersTable = value; RaisePropertyChangedAuto(); OnSelectedParametersTableChanged(); } } private Job _activeJob; /// /// Gets or sets the selected machine job. /// public Job ActiveJob { get { return _activeJob; } set { _activeJob = value; RaisePropertyChangedAuto(); } } private Job _selectedMachineJob; /// /// Gets or sets the selected machine job. /// public Job SelectedMachineJob { get { return _selectedMachineJob; } set { _selectedMachineJob = value; RaisePropertyChangedAuto(); } } private ObservableCollection _selectedJobs; /// /// Gets or sets the selected jobs. /// public ObservableCollection SelectedJobs { get { return _selectedJobs; } set { _selectedJobs = value; RaisePropertyChangedAuto(); } } private Segment _selectedSegment; /// /// Gets or sets the job selected segment. /// public Segment SelectedSegment { get { return _selectedSegment; } set { _selectedSegment = value; RaisePropertyChangedAuto(); OnSelectedSegmentChanged(); } } private ObservableCollection _selectedSegments; /// /// Gets or sets the selected segments. /// public ObservableCollection SelectedSegments { get { return _selectedSegments; } set { _selectedSegments = value; RaisePropertyChangedAuto(); } } private BrushStop _selectedBrushStop; /// /// Gets or sets the selected segment selected brush stop. /// public BrushStop SelectedBrushStop { get { return _selectedBrushStop; } set { _selectedBrushStop = value; RaisePropertyChangedAuto(); } } private ObservableCollection _selectedBrushStops; /// /// Gets or sets the selected brush stops. /// public ObservableCollection SelectedBrushStops { get { return _selectedBrushStops; } set { _selectedBrushStops = value; RaisePropertyChangedAuto(); } } private Rml _selectedRML; /// /// Gets or sets the selected RML. /// public Rml SelectedRML { get { return _selectedRML; } set { _selectedRML = value; OnSelectedRMLChanged(); RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } private bool _isSideBarOpened; /// /// Gets or sets a value indicating whether the configuration panels are opened. /// public bool IsSideBarOpened { get { return _isSideBarOpened; } set { _isSideBarOpened = value; RaisePropertyChangedAuto(); } } private TimeSpan _estimatedDuration; /// /// Gets or sets the estimated duration for the selected job. /// public TimeSpan EstimatedDuration { get { return _estimatedDuration; } set { _estimatedDuration = value; RaisePropertyChangedAuto(); } } private bool _isJobRunning; /// /// Gets or sets a value indicating whether a job is currently running. /// public bool IsJobRunning { get { return _isJobRunning; } set { _isJobRunning = value; RaisePropertyChangedAuto(); } } private Job _runningJob; /// /// Gets or sets the currently running job. /// public Job RunningJob { get { return _runningJob; } set { _runningJob = value; RaisePropertyChangedAuto(); } } private bool _isJobCompleted; /// /// Gets or sets a value indicating whether the running job has completed successfully. /// public bool IsJobCompleted { get { return _isJobCompleted; } set { _isJobCompleted = value; RaisePropertyChangedAuto(); } } private bool _isJobFailed; /// /// Gets or sets a value indicating whether the running job has failed. /// public bool IsJobFailed { get { return _isJobFailed; } set { _isJobFailed = value; RaisePropertyChangedAuto(); } } private bool _showJobStatus; /// /// Gets or sets a value indicating whether to show all the relevant job status areas. /// public bool ShowJobStatus { get { return _showJobStatus; } set { _showJobStatus = value; RaisePropertyChangedAuto(); } } private bool _isJobCanceled; /// /// Gets or sets a value indicating whether the last running job was canceled. /// public bool IsJobCanceled { get { return _isJobCanceled; } set { _isJobCanceled = value; RaisePropertyChangedAuto(); } } private IMachineOperator _machineOperator; /// /// Gets or sets the machine operator. /// public IMachineOperator MachineOperator { get { return _machineOperator; } set { _machineOperator = value; RaisePropertyChangedAuto(); } } private List _runningJobSegments; /// /// Gets or sets the running job segments. /// public List RunningJobSegments { get { return _runningJobSegments; } set { _runningJobSegments = value; RaisePropertyChangedAuto(); } } private ICollectionView _jobsCollectionView; /// /// Gets or sets the jobs collection view. /// public ICollectionView JobsCollectionView { get { return _jobsCollectionView; } set { _jobsCollectionView = value; BindingOperations.EnableCollectionSynchronization(_jobsCollectionView, _syncLock); RaisePropertyChangedAuto(); } } private ICollectionView _segmentsCollectionView; /// /// Gets or sets the segments collection view. /// public ICollectionView SegmentsCollectionView { get { return _segmentsCollectionView; } set { _segmentsCollectionView = value; RaisePropertyChangedAuto(); } } private ICollectionView _brushStopsCollectionView; /// /// Gets or sets the brush stops collection view. /// public ICollectionView BrushStopsCollectionView { get { return _brushStopsCollectionView; } set { _brushStopsCollectionView = value; BindingOperations.EnableCollectionSynchronization(_brushStopsCollectionView, _syncLock); RaisePropertyChangedAuto(); } } private String _jobFilter; /// /// Gets or sets the job filter. /// public String JobFilter { get { return _jobFilter; } set { _jobFilter = value; RaisePropertyChangedAuto(); OnJobFilterChanged(); } } private ObservableCollection _jobEvents; /// /// Gets or sets the running job events. /// public ObservableCollection JobEvents { get { return _jobEvents; } set { _jobEvents = value; RaisePropertyChangedAuto(); } } private MachinesEvent _selectedJobEvent; /// /// Gets or sets the selected job event. /// public MachinesEvent SelectedJobEvent { get { return _selectedJobEvent; } set { _selectedJobEvent = value; RaisePropertyChangedAuto(); OnSelectedJobEventChanged(); } } /// /// Gets or sets the machines providers. /// public ISuggestionProvider MachinesProvider { get; set; } /// /// Gets or sets a value indicating whether the job details view is visible. /// public bool IsJobVisible { get; set; } private bool _enableColorConversion; /// /// Gets or sets a value indicating whether to enable color conversion processes. /// public bool EnableColorConversion { get { return _enableColorConversion; } set { _enableColorConversion = value; RaisePropertyChangedAuto(); } } /// /// Gets or sets the authentication provider. /// public IAuthenticationProvider AuthenticationProvider { get; set; } /// /// Gets or sets the module settings. /// public DeveloperModuleSettings Settings { get { return _settings; } set { _settings = value; RaisePropertyChangedAuto(); } } private bool _autoProcessSelection; /// /// Gets or sets a value indicating whether [automatic process selection]. /// public bool AutoProcessSelection { get { return _autoProcessSelection; } set { _autoProcessSelection = value; RaisePropertyChangedAuto(); Settings.AutoProcessSelection = _autoProcessSelection; } } #endregion #region Commands /// /// Gets or sets the edit machine command. /// public RelayCommand EditMachineCommand { get; set; } /// /// Gets or sets the edit RML command. /// public RelayCommand EditRMLCommand { get; set; } /// /// Gets or sets the toggle side bar command. /// public RelayCommand ToggleSideBarCommand { get; set; } /// /// Gets or sets the save process parameters command. /// public RelayCommand SaveProcessParametersCommand { get; set; } /// /// Gets or sets the save liquid factors command. /// public RelayCommand SaveLiquidFactorsCommand { get; set; } /// /// Gets or sets the add segment command. /// public RelayCommand AddSegmentCommand { get; set; } /// /// Gets or sets the remove segment command. /// public RelayCommand RemoveSegmentCommand { get; set; } /// /// Gets or sets the add job command. /// public RelayCommand AddJobCommand { get; set; } /// /// Gets or sets the remove job command. /// public RelayCommand RemoveJobCommand { get; set; } /// /// Gets or sets the add brush stop command. /// public RelayCommand AddBrushStopCommand { get; set; } /// /// Gets or sets the remove brush stop command. /// public RelayCommand RemoveBrushStopCommand { get; set; } /// /// Gets or sets the save job command. /// public RelayCommand SaveJobCommand { get; set; } /// /// Gets or sets the discard job command. /// public RelayCommand DiscardJobCommand { get; set; } /// /// Gets or sets the start job command. /// public RelayCommand StartJobCommand { get; set; } /// /// Gets or sets the start job and record command. /// public RelayCommand StartJobAndRecordCommand { get; set; } /// /// Gets or sets the stop job command. /// public RelayCommand StopJobCommand { get; set; } /// /// Gets or sets the close job completion status command. /// public RelayCommand CloseJobCompletionStatusCommand { get; set; } /// /// Gets or sets the load job command. /// public RelayCommand LoadJobCommand { get; set; } /// /// Gets or sets the duplicate job command. /// public RelayCommand DuplicateJobCommand { get; set; } /// /// Gets or sets the duplicate segment command. /// public RelayCommand DuplicateSegmentCommand { get; set; } /// /// Gets or sets the duplicate brush stop command. /// public RelayCommand DuplicateBrushStopCommand { get; set; } /// /// Gets or sets the push process parameters command. /// public RelayCommand PushProcessParametersCommand { get; set; } /// /// Gets or sets the import embroidery file command. /// public RelayCommand ImportEmbroideryFileCommand { get; set; } /// /// Gets or sets the display job embroidery file command. /// public RelayCommand DisplayJobEmbroideryFileCommand { get; set; } /// /// Gets or sets the reload machines command. /// public RelayCommand ReloadMachinesCommand { get; set; } /// /// Gets or sets the back to job command. /// public RelayCommand BackToJobCommand { get; set; } /// /// Gets or sets to running job command. /// public RelayCommand ToRunningJobCommand { get; set; } /// /// Gets or sets the reset process parameters command. /// public RelayCommand ResetProcessParametersCommand { get; set; } /// /// Gets or sets the import job file command. /// public RelayCommand ImportJobFileCommand { get; set; } /// /// Gets or sets the export job file command. /// public RelayCommand ExportJobFileCommand { get; set; } #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The application manager. /// The notification provider. public MainViewVM(IStudioApplicationManager applicationManager, INotificationProvider notificationProvider, IDiagnosticsFrameProvider diagnosticsFrameProvider, IVideoCaptureProvider videoCaptureProvider, DeveloperNavigationManager navigation, INavigationManager navigationManager, IAuthenticationProvider authentication, IEventLogger eventLogger, ISpeechProvider speech, IActionLogManager actionLogManager) { _converter = new DefaultColorConverter(); _cctCache = new List(); CanWork = true; EnableColorConversion = true; AuthenticationProvider = authentication; _actionLogManager = actionLogManager; _notification = notificationProvider; _speech = speech; _navigation = navigation; _msNavigation = navigationManager; ApplicationManager = applicationManager; VideoCaptureProvider = videoCaptureProvider; _eventLogger = eventLogger; LogManager.Log("Initializing relay commands..."); TangoIOC.Default.GetInstanceWhenAvailable((vm) => { _dataCaptureVM = vm; _dataCaptureVM.RelayCommandsInvalidated += (_, __) => StartJobAndRecordCommand.RaiseCanExecuteChanged(); }); //Initialize Commands... EditMachineCommand = new RelayCommand(EditMachine, () => SelectedMachine != null && CanWork); EditRMLCommand = new RelayCommand(EditRML, () => SelectedRML != null && CanWork); ToggleSideBarCommand = new RelayCommand(() => IsSideBarOpened = !IsSideBarOpened); SaveLiquidFactorsCommand = new RelayCommand(SaveLiquidFactors, () => SelectedRML != null && CanWork); AddSegmentCommand = new RelayCommand(AddSegment, () => ActiveJob != null && CanWork); RemoveSegmentCommand = new RelayCommand(RemoveSelectedSegments, () => SelectedSegment != null && CanWork); AddJobCommand = new RelayCommand(AddJob, () => SelectedMachine != null && CanWork); RemoveJobCommand = new RelayCommand(RemoveSelectedJobs, () => SelectedMachineJob != null && CanWork); AddBrushStopCommand = new RelayCommand(AddBrushStop, () => SelectedSegment != null && CanWork); RemoveBrushStopCommand = new RelayCommand(RemoveSelectedBrushStops, () => SelectedBrushStop != null && CanWork); SaveJobCommand = new RelayCommand(SaveActiveJob, () => SelectedMachine != null && CanWork); DiscardJobCommand = new RelayCommand(BackToJobs, () => SelectedMachine != null && CanWork); StartJobCommand = new RelayCommand(() => StartJob(), () => ActiveJob != null && CanWork && !IsJobRunning && MachineOperator != null); StartJobAndRecordCommand = new RelayCommand(StartJobAndRecord, () => _dataCaptureVM != null && !_dataCaptureVM.Recorder.IsRecording && !_dataCaptureVM.Player.IsPlaying && ActiveJob != null && !IsJobRunning && MachineOperator != null && CanWork); StopJobCommand = new RelayCommand(StopJob, () => IsJobRunning && CanWork); CloseJobCompletionStatusCommand = new RelayCommand(CloseJobCompletionStatusBar); LoadJobCommand = new RelayCommand(() => LoadSelectedJob(), () => SelectedMachineJob != null && CanWork); DuplicateJobCommand = new RelayCommand(DuplicateSelectedJobs, () => SelectedMachineJob != null && CanWork); DuplicateSegmentCommand = new RelayCommand(DuplicateSelectedSegments, () => SelectedSegment != null && CanWork); DuplicateBrushStopCommand = new RelayCommand(DuplicateSelectedBrushStops, () => SelectedBrushStop != null && CanWork); SaveProcessParametersCommand = new RelayCommand(SaveProcessParameters, () => SelectedRML != null && CanWork && SelectedRML.ProcessParametersTablesGroups.Count > 0); PushProcessParametersCommand = new RelayCommand(PushProcessParameters, () => SelectedRML != null && CanWork && SelectedRML.ProcessParametersTablesGroups.Count > 0 && SelectedProcessParametersTable != null && MachineOperator != null); ImportEmbroideryFileCommand = new RelayCommand(ImportEmbroideryFile, () => SelectedMachine != null && CanWork); DisplayJobEmbroideryFileCommand = new RelayCommand(DisplayJobEmbroideryFile, () => CanWork); ReloadMachinesCommand = new RelayCommand(() => LoadMachine(), () => CanWork && SelectedMachine != null); ResetProcessParametersCommand = new RelayCommand(ResetProcessParameters, () => CanWork && MachineOperator != null); ImportJobFileCommand = new RelayCommand(ImportJobFile, () => SelectedMachine != null && CanWork); ExportJobFileCommand = new RelayCommand(ExportJobFile, () => SelectedMachine != null && SelectedMachineJob != null && CanWork); ApplicationManager.ConnectedMachineChanged += ApplicationManager_ConnectedMachineChanged; _eventLogger.NewLog += _eventLogger_NewLog; MachinesProvider = new SuggestionProvider((filter) => { try { return _machineDbContext.Machines.Where(x => x.SerialNumber.StartsWith(filter)).ToList(); } catch { return null; } }); BackToJobCommand = new RelayCommand(BackToJob); ToRunningJobCommand = new RelayCommand(ToRunningJob); } #endregion #region Application Ready public override void OnApplicationReady() { Settings = SettingsManager.Default.GetOrCreate(); AutoProcessSelection = Settings.AutoProcessSelection; SelectedJobs = new ObservableCollection(); JobEvents = new ObservableCollection(); LogManager.Log("Initializing machine Db context..."); _machineDbContext = ObservablesContext.CreateDefault(); if (_settings.LastSelectedMachineGuid != null) { LogManager.Log("Setting last selected machine from settings..."); SelectedMachine = _machineDbContext.Machines.SingleOrDefault(x => x.Guid == _settings.LastSelectedMachineGuid); } if (_settings.LastSelectedJobGuid != null && SelectedMachine != null) { LogManager.Log("Setting last selected job from settings..."); SelectedMachineJob = SelectedMachine.Jobs.SingleOrDefault(x => x.Guid == _settings.LastSelectedJobGuid); } _colorConversionThread = new Thread(ColorConversionThreadMethod); _colorConversionThread.IsBackground = true; _colorConversionThread.Start(); } #endregion #region Color Conversion [HandleProcessCorruptedStateExceptions] private void ColorConversionThreadMethod() { while (true) { if (!_rml_has_no_cct && EnableColorConversion && !_disable_gamut_check && IsJobVisible && IsVisible && ActiveJob != null && ActiveJob.Segments != null && SelectedProcessParametersTable != null) { try { var stops = ActiveJob.Segments.SelectMany(x => x.BrushStops).Where(x => !x.Corrected && !x.OutOfGamutChecked).ToList(); foreach (var stop in stops) { if (stop.ColorSpace.Code == BL.Enumerations.ColorSpaces.Volume.ToInt32()) { try { var output = _converter.Convert(stop, false, true); stop.Red = output.SingleCoordinates.Red; stop.Green = output.SingleCoordinates.Green; stop.Blue = output.SingleCoordinates.Blue; stop.Corrected = true; stop.IsOutOfGamut = false; } catch (Exception ex) { Debug.WriteLine(ex.ToString()); } } else if (stop.ColorSpace.Code == BL.Enumerations.ColorSpaces.RGB.ToInt32() || stop.ColorSpace.Code == BL.Enumerations.ColorSpaces.LAB.ToInt32()) { try { var output = _converter.Convert(stop, false, true); output.ApplyOnBrushStopLiquidVolumes(stop, SelectedProcessParametersTable); stop.OutOfGamutChecked = true; } catch { } } } } catch { } } if (AutoProcessSelection && IsJobVisible && IsVisible && ActiveJob != null && ActiveJob.Segments != null && !_rml_has_no_cct && !_disable_gamut_check) { try { bool useLightInks = true; if (ActiveJob.OrderedSegmentsWithGroups.Count > 1 && !ActiveJob.EnableInterSegment) useLightInks = false; var recommendedProcess = _converter.GetRecommendedProcessParameters(ActiveJob, RmlProcessParametersTableGroup, useLightInks); if (recommendedProcess != null && recommendedProcess != SelectedProcessParametersTable) { SelectedProcessParametersTable = recommendedProcess; } } catch (Exception ex) { LogManager.Log(ex, "Error resolving recommended process parameters."); } } Thread.Sleep(500); } } public void OnHivePopupOpened() { if (SelectedBrushStop != null) { _hiveOpened = true; try { HiveSuggestions = _converter.Convert(SelectedBrushStop, true, true).CreateHiveSuggestions(); } catch (Exception ex) { _hiveOpened = false; LogManager.Log(ex); _notification.ShowError($"Error occurred while trying to convert the source color.\n{ex.Message}"); } } } private void OnSelectedSuggestionChanged() { if (SelectedSuggestion != null && SelectedBrushStop != null && _hiveOpened) { _color_changed_from_hive = true; SelectedBrushStop.Color = SelectedSuggestion.Color; SelectedBrushStop.Corrected = true; SelectedBrushStop.IsOutOfGamut = false; var coords = SelectedSuggestion.Coordinates; foreach (var liquid in coords.OutputLiquids) { var liquidVolume = SelectedBrushStop.LiquidVolumes.SingleOrDefault(x => x.IdsPack.LiquidType.Code == liquid.LiquidType.ToInt32()); if (liquidVolume != null) { liquidVolume.Volume = liquid.Volume; } } _color_changed_from_hive = false; } } public void OnSelectedBrushColorChanged(Color color) { if (!_color_changed_from_hive && _hiveOpened) { SelectedBrushStop.Corrected = false; HiveSuggestions = _converter.Convert(SelectedBrushStop, true, true).CreateHiveSuggestions(); } } public void OnHivePopupClosed() { _hiveOpened = false; } /// /// Called when the brush stop field value has been changed (This called from the view!). /// /// The brush stop. public void OnBrushStopFieldValueChanged(BrushStop brushStop) { brushStop.Corrected = false; brushStop.OutOfGamutChecked = false; } #endregion #region Event Handlers private void _eventLogger_NewLog(object sender, MachinesEvent e) { if (IsJobRunning) { InvokeUI(() => { JobEvents.Insert(0, e); }); } } /// /// Handles the application manager connected machine changes event. /// /// The sender. /// The machine. private void ApplicationManager_ConnectedMachineChanged(object sender, IExternalBridgeClient machine) { MachineOperator = machine; if (MachineOperator != null) { MachineOperator.MachineEventsStateProvider.EventsChanged -= MachineEventsStateProvider_EventsChanged; MachineOperator.MachineEventsStateProvider.EventsChanged += MachineEventsStateProvider_EventsChanged; MachineOperator.MachineEventsStateProvider.NewEvents -= MachineEventsStateProvider_NewEvents; MachineOperator.MachineEventsStateProvider.NewEvents += MachineEventsStateProvider_NewEvents; MachineOperator.ResumingJob -= MachineOperator_ResumingJob; MachineOperator.ResumingJob += MachineOperator_ResumingJob; MachineOperator.PreparingJobProgress -= MachineOperator_PreparingJobProgress; MachineOperator.PreparingJobProgress += MachineOperator_PreparingJobProgress; } } private void MachineOperator_PreparingJobProgress(object sender, PreparingJobProgressEventArgs e) { var percent = (e.Progress / e.Total * 100d); if (_preparingTaskItem != null) { _preparingTaskItem.Message = $"Preparing job for printing {(e.Progress / e.Total * 100d).ToString("0.0")}%..."; } if (_preparingTaskItem == null && percent == 0) { _preparingTaskItem = _notification.PushTaskItem("Preparing job for printing..."); } else if (percent == 100 && _preparingTaskItem != null) { _preparingTaskItem.Pop(); _preparingTaskItem = null; } } private void MachineOperator_ResumingJob(object sender, ResumingJobEventArgs e) { if (_notification.ShowQuestion("Machine studio has detected a job in progress. Would you like to try and continue from there you were?")) { var job = _machineDbContext.Jobs.SingleOrDefault(x => x.Guid == e.JobGuid); if (job != null) { _msNavigation.NavigateToModule(); SelectedMachine = _machineDbContext.Machines.SingleOrDefault(x => x.Guid == job.MachineGuid); SelectedMachineJob = SelectedMachine.Jobs.SingleOrDefault(x => x.Guid == job.Guid); LoadSelectedJob(() => { StartJob(e.Approve); }); } else { LogManager.Log($"Could not resume job. The running job with guid '{e.JobGuid}' was not found."); _notification.ShowError("Could not resume job. The running job was not found."); } } } private void MachineEventsStateProvider_NewEvents(object sender, IEnumerable events) { HandleNewHardwareEvents(events); } private void MachineEventsStateProvider_EventsChanged(object sender, IEnumerable changedEvents) { InvokeUI(StartJobCommand.RaiseCanExecuteChanged); InvokeUI(StartJobAndRecordCommand.RaiseCanExecuteChanged); } /// /// Handles the Saved event of the SelectedMachine. /// /// The source of the event. /// The instance containing the event data. private void SelectedMachine_Modified(object sender, ObservableModifiedEventArgs e) { if (e.IsOtherContext) { InvokeUI(() => { SelectedMachine.Reload(_machineDbContext); InvalidateLiquidFactorsAndProcessTables(); if (SelectedSegment != null) { OnSelectedSegmentChanged(); } }); } } /// /// Handles the LengthChanged event of the SelectedJob. /// /// The source of the event. /// The instance containing the event data. private void ActiveJob_LengthChanged(object sender, EventArgs e) { UpdateEstimatedDuration(); } /// /// Handles the DyeingSpeedMinInkUptakeChanged event of the SelectedProcessParametersTable. /// /// The source of the event. /// The instance containing the event data. private void SelectedProcessParametersTable_DyeingSpeedMinInkUptakeChanged(object sender, EventArgs e) { if (SelectedSegment != null) { foreach (var liquidVolume in SelectedSegment.BrushStops.SelectMany(x => x.LiquidVolumes)) { liquidVolume.Invalidate(); } } UpdateEstimatedDuration(); } #endregion #region Hardware Events private void HandleNewHardwareEvents(IEnumerable events) { if (IsJobRunning) { _speech.SpeakError(events.Last().EventType.Name); //if (events.ToList().Exists(x => x.Actions.Contains(BL.Enumerations.EventTypeActions.StopJob))) //{ // if (JobHandler != null) // { // InvokeUI(StopJob); // } //} } } #endregion #region Properties Changes /// /// Called when the selected parameters table has changed. /// protected virtual void OnSelectedParametersTableChanged() { if (SelectedProcessParametersTable != null) { LogManager.Log("Selected process parameters table changed."); SelectedProcessParametersTable.DyeingSpeedMinInkUptakeChanged -= SelectedProcessParametersTable_DyeingSpeedMinInkUptakeChanged; SelectedProcessParametersTable.DyeingSpeedMinInkUptakeChanged += SelectedProcessParametersTable_DyeingSpeedMinInkUptakeChanged; foreach (var segment in ActiveJob.Segments) { SetSegmentBrushStopsLiquidVolumes(segment); } UpdateEstimatedDuration(); } } /// /// Called when the process parameters table group has been changed /// protected virtual void OnProcessParametersTableGroupChanged() { if (RmlProcessParametersTableGroup != null && RmlProcessParametersTableGroup.ProcessParametersTables.Count > 0) { LogManager.Log("Process parameters group changed..."); InvokeUI(() => { SelectedProcessParametersTable = RmlProcessParametersTableGroup.ProcessParametersTables.OrderBy(x => x.TableIndex).FirstOrDefault(); UpdateEstimatedDuration(); }); } } /// /// Called when the selected segment has been changed /// protected virtual void OnSelectedSegmentChanged() { if (SelectedSegment != null) { LogManager.Log("Selected segment changed..."); SetSegmentBrushStopsLiquidVolumes(SelectedSegment); SelectedBrushStop = SelectedSegment.BrushStops.FirstOrDefault(); BrushStopsCollectionView = CollectionViewSource.GetDefaultView(SelectedSegment.BrushStops); BrushStopsCollectionView.SortDescriptions.Add(new SortDescription(nameof(BrushStop.StopIndex), ListSortDirection.Ascending)); } } /// /// Called when the selected group history has been changed /// protected virtual void OnSelectedGroupHistoryChanged() { if (SelectedGroupHistory != null) { LogManager.Log(String.Format("Parameters group {0} selected from history.", SelectedGroupHistory.Name)); RmlProcessParametersTableGroup = SelectedGroupHistory.Clone(); } } /// /// Called when the machine has been changed /// protected virtual void OnSelectedMachineChanged() { if (SelectedMachine != null) { LogManager.Log(String.Format("Machine {0} changed.", SelectedMachine.SerialNumber)); LoadMachine(); } } /// /// Called when the job filtering has changed. /// protected virtual void OnJobFilterChanged() { String filter = JobFilter.ToLower(); if (JobsCollectionView == null) return; JobsCollectionView.Filter = (job) => { try { Job j = job as Job; return String.IsNullOrWhiteSpace(filter) || j.Name.ToLower().Contains(filter) //Job name || (j.User != null && j.User.Contact.FirstName.ToLower().Contains(filter)) // User first name || j.Length.ToString().Contains(filter); //Job length } catch (Exception ex) { LogManager.Log(ex, "Error filtering jobs."); return true; } }; } #endregion #region Drag & Drop /// /// Switch the segment position in the job. /// /// The dragged. /// The dropped. public void OnDropSegment(Segment dragged, Segment dropped) { LogManager.Log(String.Format("Segment {0} Dropped on segment {1}", dragged.SegmentIndex, dropped.SegmentIndex)); dragged.SegmentIndex = dropped.SegmentIndex; dropped.SegmentIndex++; int index = 1; foreach (var segment in ActiveJob.Segments.OrderBy(x => x.SegmentIndex)) { segment.SegmentIndex = index++; } SegmentsCollectionView.Refresh(); } /// /// Switch the brush stop position in the segment. /// /// The dragged stop. /// The dropped stop. public void OnDropBrushStop(BrushStop dragged, BrushStop dropped) { LogManager.Log(String.Format("BrushStop {0} Dropped on BrushStop {1}", dragged.StopIndex, dropped.StopIndex)); dragged.SetStopIndexNoRaise(dropped.StopIndex); dropped.SetStopIndexNoRaise(dropped.StopIndex + 1); ArrangeBrushStopsIndices(); } #endregion #region Running Job Management private void OnSelectedJobEventChanged() { if (SelectedJobEvent != null && SelectedJobEvent.Type != BL.Enumerations.EventTypes.APPLICATION_STARTED && !_dialog_shown) { _dialog_shown = true; _notification.ShowModalDialog(new EventDetailsViewVM(SelectedJobEvent), (x) => { }, () => { _dialog_shown = false; }); } } /// /// Closes the job completion status bar. /// private void CloseJobCompletionStatusBar() { LogManager.Log("Closing job completion status bar..."); _navigation.NavigateTo(DeveloperNavigationView.JobView); IsJobCompleted = false; IsJobFailed = false; IsJobCanceled = false; ShowJobStatus = false; RunningJob = null; } /// /// Stops the job. /// private void StopJob() { LogManager.Log("Stopping job..."); IsJobRunning = false; IsJobCanceled = true; JobHandler.Cancel(); } /// /// Fails the job. /// private void SetJobFailed() { if (IsJobRunning) { LogManager.Log("Setting job failed state..."); IsJobRunning = false; IsJobFailed = true; _speech.SpeakError("Job Failed!"); } } /// /// Completes the job. /// private void SetJobCompleted() { LogManager.Log("Setting job completed state..."); IsJobRunning = false; IsJobCompleted = true; _speech.SpeakInfo("Job Completed!"); } /// /// Starts the job. /// private async void StartJob(Func resumeFunc = null) { SettingsManager.Default.Save(); LogManager.Log(String.Format("Starting job {0}...", ActiveJob.Name)); if (MachineOperator == null || MachineOperator.State != TransportComponentState.Connected) { _notification.ShowError("No machine connected. Could not execute the specified job."); return; } if (SelectedProcessParametersTable == null) { _notification.ShowError("No process parameters table selected. Could not execute the specified job."); return; } foreach (var stop in ActiveJob.Segments.SelectMany(x => x.BrushStops).Where(x => x.LiquidVolumes == null)) { stop.SetLiquidVolumes(SelectedMachine.Configuration, SelectedRML, SelectedProcessParametersTable); } if (AutoProcessSelection) { LogManager.Log("Auto process parameters selection enabled. Trying to resolve the recommended process parameters..."); try { var recommendedProcess = _converter.GetRecommendedProcessParameters(ActiveJob, RmlProcessParametersTableGroup); if (recommendedProcess != null && recommendedProcess != SelectedProcessParametersTable) { SelectedProcessParametersTable = recommendedProcess; } } catch (Exception ex) { LogManager.Log(ex, "Error resolving recommended process parameters."); _notification.ShowError("An error occurred while trying to resolve the recommended process parameters.Please try to disable the auto selection."); return; } } JobEvents.Clear(); IsJobFailed = false; IsJobCanceled = false; IsJobCompleted = false; RunningJob = ActiveJob; _runningJobEstimatedDuration = EstimatedDuration; RunningJobSegments = RunningJob.EffectiveSegments.ToList(); try { IsFree = false; LogManager.Log("Sending job to machine operator..."); MachineOperator.GradientGenerationConfiguration.IsEnabled = Settings.EnableGradientGeneration; MachineOperator.GradientGenerationConfiguration.ResolutionCM = Settings.GradientResolutionCM; if (resumeFunc == null) { JobHandler = await MachineOperator.Print(ActiveJob, SelectedProcessParametersTable); } else { JobHandler = resumeFunc(); } _navigation.NavigateTo(DeveloperNavigationView.RunningJobView); IsJobRunning = true; ShowJobStatus = true; JobHandler.StatusChanged += (x, status) => { if (IsJobRunning) { RunningJobStatus = status; if (status.Message != null) { // TODO: Write to db when shlomo is not sending test messages anymore. _eventLogger.Log(BL.Enumerations.EventTypes.JOB_STATUS, status.Message, false); } } }; JobHandler.SegmentStarted += (x, segment) => { if (!segment.IsInterSegment) { _speech.SpeakInfo(String.Format("Segment {0} Started.", segment.SegmentIndex)); } else { _speech.SpeakInfo(String.Format("Inter Segment Started.")); } }; JobHandler.UnitCompleted += (x, unit) => { _speech.SpeakInfo(String.Format("{0} Units Completed.", unit + 1)); }; JobHandler.Failed += (x, ex) => { LogManager.Log(ex, String.Format("Job {0} has failed.", RunningJob.Name)); SetJobFailed(); InvokeUI(() => { _notification.ShowError("Job failed. " + ex.FlattenMessage()); StopRecordingIfInProgress(); }); }; JobHandler.Finalizing += (_, __) => { _speech.SpeakInfo("Finalizing job..."); LogManager.Log(String.Format("Finalizing job {0}.", RunningJob.Name)); }; JobHandler.Completed += (x, e) => { LogManager.Log(String.Format("Job {0} has completed.", RunningJob.Name)); SetJobCompleted(); StopRecordingIfInProgress(); }; JobHandler.Canceled += (x, y) => { if (_preparingTaskItem != null) { _preparingTaskItem.Pop(); _preparingTaskItem = null; } LogManager.Log(String.Format("Job {0} has been canceled.", RunningJob.Name)); StopRecordingIfInProgress(); //Finally Canceled.. }; } catch (InsufficientLiquidQuantityException ex) { _notification.ShowModalDialog(new InsufficientLiquidQuantityViewVM(ex), (x) => { MachineOperator.EnableJobLiquidQuantityValidation = false; StartJob(); }, () => { }); } catch (Exception ex) { LogManager.Log(ex); _notification.ShowError("An error occurred while starting the job. " + Environment.NewLine + ex.Message); SetJobFailed(); StopRecordingIfInProgress(); } finally { IsFree = true; } } /// /// Starts the job and record using the data capture module. /// private void StartJobAndRecord() { _isRecording = true; _dataCaptureVM.StartDiagnosticsRecording(); StartJob(); } /// /// Stops the recording if in progress. /// private void StopRecordingIfInProgress() { if (_isRecording) { _isRecording = false; InvokeUI(() => _dataCaptureVM.StopRecorderOrPlayer()); } } private void BackToJob() { _navigation.NavigateTo(DeveloperNavigationView.JobView); } private void ToRunningJob() { _navigation.NavigateTo(DeveloperNavigationView.RunningJobView); } #endregion #region RML /// /// Saves the liquid factors. /// private async void SaveLiquidFactors() { if (SelectedRML != null) { CanWork = false; using (_notification.PushTaskItem("Saving Liquid Factors...")) { LogManager.Log(String.Format("Saving liquid factors for RML {0}...", SelectedRML.Name)); await SelectedRML.SaveAsync(_activeJobDbContext); var rmlAfterChange = RmlDTO.FromObservable(SelectedRML); _actionLogManager.InsertLog(ActionLogType.RmlSaved, AuthenticationProvider.CurrentUser, SelectedRML.Name, _selectedRMLBeforeLiquidFactorsSaves, rmlAfterChange, "RML liquid factors changed from Machine Studio Research module."); _selectedRMLBeforeLiquidFactorsSaves = rmlAfterChange; LiquidTypesRmls = ActiveJob.Machine.Configuration.GetSupportedIdsPacks(SelectedRML).Select(x => x.LiquidType).SelectMany(x => x.LiquidTypesRmls).Where(x => x.Rml.Guid == SelectedRML.Guid).ToList(); foreach (var segment in ActiveJob.Segments) { SetSegmentBrushStopsLiquidVolumes(segment); } } CanWork = true; } } /// /// Navigates to the DB Module in order to edit the selected RML. /// private void EditRML() { LogManager.Log(String.Format("Requesting DB module for RML {0} editing...", SelectedRML.Name)); } /// /// Invalidates the liquid factors and process parameters tables. /// private void InvalidateLiquidFactorsAndProcessTables() { if (SelectedRML != null && SelectedMachine != null) { LogManager.Log("Invalidating liquid factors, process parameters and process group history..."); _selectedRML = new RmlBuilder(_activeJobDbContext).Set(SelectedRML).WithAllParametersGroup().WithCAT(SelectedMachine.Guid).WithCctCache(_cctCache).WithCCT().WithGbdAndLub().WithLiquidFactors().WithSpools().Build(); _selectedRMLBeforeLiquidFactorsSaves = RmlDTO.FromObservable(_selectedRML); if (_selectedRML.Cct == null) { InvokeUI(() => { _rml_has_no_cct = true; _notification.ShowWarning(LogManager.Log($"No color conversion table defined for the selected RML '{_selectedRML.Name}'. Color conversion is disabled.", LogCategory.Warning)); }); } else { _rml_has_no_cct = false; } LiquidTypesRmls = ActiveJob.Machine.Configuration.GetSupportedIdsPacks(SelectedRML).Select(x => x.LiquidType).SelectMany(x => x.LiquidTypesRmls).Where(x => x.Rml.Guid == SelectedRML.Guid).ToList(); RmlProcessParametersTableGroup = SelectedRML.ProcessParametersTablesGroups.ToList().SingleOrDefault(x => x.Active); var selectedHistory = RmlProcessParametersTableGroup; if (RmlProcessParametersTableGroup != null) { RmlProcessParametersTableGroup = RmlProcessParametersTableGroup.Clone(); RmlProcessParametersTableGroup.ProcessParametersTables = RmlProcessParametersTableGroup.ProcessParametersTables.OrderBy(x => x.TableIndex).ToSynchronizedObservableCollection(); } GroupsHistory = SelectedRML.ProcessParametersTablesGroups.OrderByDescending(x => x.SaveDate).OrderBy(x => !x.Active).ToObservableCollection(); _selectedGroupHistory = selectedHistory; InvokeUI(() => { RaisePropertyChanged(nameof(SelectedGroupHistory)); RaisePropertyChanged(nameof(RmlProcessParametersTableGroup)); }); ActiveJob.Rml = SelectedRML; } } private async void OnSelectedRMLChanged() { if (SelectedRML != null && SelectedMachine != null) { using (_notification.PushTaskItem("Loading RML...")) { await Task.Factory.StartNew(() => { try { IsFree = false; InvalidateLiquidFactorsAndProcessTables(); } catch { } finally { IsFree = true; } }); } } } #endregion #region Color Space public void OnBrushStopColorSpaceChanged(BrushStop stop) { if (stop != null && stop.ColorSpace != null && stop.BrushColorSpace != BL.Enumerations.ColorSpaces.Volume) { var lubricant = stop.LiquidVolumes.SingleOrDefault(x => x.LiquidType == LiquidTypes.Lubricant); if (lubricant != null) { lubricant.Volume = 100; } } } #endregion #region Process Parameters Management /// /// Uploads the selected process parameters table. /// private async void PushProcessParameters() { using (_notification.PushTaskItem("Uploading Process Parameters...")) { try { LogManager.Log($"Uploading process parameters table {SelectedProcessParametersTable.Name}..."); await MachineOperator.UploadProcessParameters(SelectedProcessParametersTable); } catch (Exception ex) { LogManager.Log(ex, $"Failed to upload process parameters table {SelectedProcessParametersTable.Name}"); _notification.ShowError("Failed to upload the selected process parameters." + Environment.NewLine + ex.Message); } } } /// /// Saves the process parameters group. /// private async void SaveProcessParameters() { var response = _notification.ShowTextInput("Enter Group Name", "Group Name"); if (response == null) return; CanWork = false; using (_notification.PushTaskItem("Saving Parameters Group...")) { var processGroupBefore = ProcessParametersTablesGroupDTO.FromObservable(SelectedRML.GetActiveProcessGroup()); using (var db = ObservablesContext.CreateDefault()) { var active_groups = db.ProcessParametersTablesGroups.Where(x => x.RmlGuid == SelectedRML.Guid && x.Active).ToList(); foreach (var g in active_groups) { g.Active = false; } await db.SaveChangesAsync(); } LogManager.Log(String.Format("Saving process parameters group under the name {0}...", response)); ProcessParametersTablesGroup group = new ProcessParametersTablesGroup(); List tables = new List(); int index = 0; foreach (var table in RmlProcessParametersTableGroup.ProcessParametersTables) { var newTable = table.Clone(); newTable.TableIndex = index++; newTable.ProcessParametersTablesGroup = group; tables.Add(newTable); } group.Active = true; group.ProcessParametersTables = tables.ToSynchronizedObservableCollection(); group.Rml = SelectedRML; group.Name = response; group.SaveDate = DateTime.UtcNow; foreach (var g in SelectedRML.ProcessParametersTablesGroups) { g.Active = false; } SelectedRML.ProcessParametersTablesGroups.Add(group); await SelectedRML.SaveAsync(_activeJobDbContext); _actionLogManager.InsertLog(ActionLogType.RmlActiveProcessParametersChanged, AuthenticationProvider.CurrentUser, SelectedRML.Name, processGroupBefore, ProcessParametersTablesGroupDTO.FromObservable(SelectedRML.GetActiveProcessGroup()), "RML Active process parameters changed from Machine Studio Research module."); InvalidateLiquidFactorsAndProcessTables(); } CanWork = true; } /// /// Resets the process parameters. /// private async void ResetProcessParameters() { if (_notification.ShowQuestion("This will reset the process parameters. Are you sure?")) { using (_notification.PushTaskItem("Resetting process parameters...")) { try { await ApplicationManager.ConnectedMachine.UploadProcessParameters(new ProcessParametersTable()); _notification.ShowInfo("Heaters are turned off."); } catch (Exception ex) { LogManager.Log(ex, "Error resetting process parameters."); _notification.ShowError("Error resetting process parameters." + Environment.NewLine + ex.Message); } } } } #endregion #region Active Job Management /// /// Loads the selected job. /// private async void LoadSelectedJob(Action onCompleted = null) { if (SelectedMachineJob != null) { CanWork = false; using (_notification.PushTaskItem("Loading job details...")) { try { await Task.Factory.StartNew(() => { _disable_gamut_check = true; LogManager.Log(String.Format("Loading job {0}...", SelectedMachineJob.Name)); SelectedSegments = new ObservableCollection(); SelectedBrushStops = new ObservableCollection(); SelectedRML = null; SelectedSegment = null; SelectedGroupHistory = null; SelectedBrushStop = null; SelectedProcessParametersTable = null; RmlProcessParametersTableGroup = null; _blockInvalidateCommands = false; LogManager.Log("Creating active job DB context..."); _activeJobDbContext = ObservablesContext.CreateDefault(); LogManager.Log("Initializing available color spaces, RMLs & Winding methods..."); //var processParamsGroups = _activeJobDbContext.ProcessParametersTablesGroups.ToList(); //var processParams = _activeJobDbContext.ProcessParametersTables.ToList(); ColorSpaces = _activeJobDbContext.ColorSpaces.ToObservableCollection(); Rmls = _activeJobDbContext.Rmls.OrderBy(i => i.Name).ToObservableCollection(); WindingMethods = _activeJobDbContext.WindingMethods.ToObservableCollection(); SpoolTypes = _activeJobDbContext.SpoolTypes.ToObservableCollection(); LogManager.Log("Loading machine spools..."); _activeJobDbContext.Spools.Where(x => x.MachineGuid == SelectedMachine.Guid).Load(); LogManager.Log("Setting active job..."); ActiveJob = new JobBuilder(_activeJobDbContext).Set(SelectedMachineJob.Guid).WithUser().WithSegments().WithBrushStops().WithConfiguration().WithRML(_cctCache).Build(); //_activeJobDbContext.Ccts.Where(x => x.RmlGuid == ActiveJob.RmlGuid).ToList(); //_activeJobDbContext.Cats.Where(x => x.RmlGuid == ActiveJob.RmlGuid).ToList(); //_activeJobDbContext.Machines.SingleOrDefault(x => x.Guid == ActiveJob.MachineGuid); //_activeJobDbContext.Configurations.SingleOrDefault(x => x.Guid == ActiveJob.Machine.ConfigurationGuid); //_activeJobDbContext.LiquidTypesRmls.ToList(); //_activeJobDbContext.IdsPackFormulas.ToList(); //_activeJobDbContext.LiquidTypes.ToList(); //_activeJobDbContext.MidTankTypes.ToList(); //_activeJobDbContext.DispenserTypes.ToList(); //_activeJobDbContext.IdsPacks.Where(x => x.ConfigurationGuid == ActiveJob.Machine.ConfigurationGuid).ToList(); _beforeSaveJobDTO = JobDTO.FromObservable(ActiveJob); LogManager.Log("Setting selected segment..."); _selectedSegment = ActiveJob.OrderedSegments.FirstOrDefault(); ActiveJob.LengthChanged -= ActiveJob_LengthChanged; ActiveJob.LengthChanged += ActiveJob_LengthChanged; _selectedRML = ActiveJob.Rml; InvalidateLiquidFactorsAndProcessTables(); RaisePropertyChanged(nameof(SelectedRML)); UpdateEstimatedDuration(); _blockInvalidateCommands = false; InvalidateRelayCommands(); _disable_gamut_check = false; _settings.LastSelectedMachineGuid = SelectedMachine != null ? SelectedMachine.Guid : null; _settings.LastSelectedJobGuid = SelectedMachineJob != null ? SelectedMachineJob.Guid : null; _settings.Save(); }); SegmentsCollectionView = CollectionViewSource.GetDefaultView(ActiveJob.Segments); SegmentsCollectionView.SortDescriptions.Add(new SortDescription(nameof(Segment.SegmentIndex), ListSortDirection.Ascending)); foreach (var segment in ActiveJob.Segments) { SetSegmentBrushStopsLiquidVolumes(segment); } SelectedSegment = _selectedSegment; if (ActiveJob != null) { _current_job_string = ActiveJob.ToJobFileWhenLoaded().ToString(); } UIHelper.DoEvents(); _navigation.NavigateTo(DeveloperNavigationView.JobView); } catch (Exception ex) { LogManager.Log(ex, "Error loading job."); _notification.ShowError($"An error occurred while trying to load the selected job.\n{ex.FlattenMessage()}"); } finally { CanWork = true; } } CanWork = true; onCompleted?.Invoke(); } } /// /// Saves the active job. /// private async void SaveActiveJob() { if (ActiveJob != null) { CanWork = false; try { using (_notification.PushTaskItem("Saving job details...")) { await Task.Factory.StartNew(() => { LogManager.Log(String.Format("Saving the active job {0}...", ActiveJob.Name)); ActiveJob.LastUpdated = DateTime.UtcNow; ActiveJob.Version = 1; ActiveJob.IsSynchronized = false; ActiveJob.Rml = SelectedRML; ActiveJob.EstimatedDurationMili = (int)EstimatedDuration.TotalMilliseconds; ActiveJob.MarkModified(_activeJobDbContext); _activeJobDbContext.SaveChanges(); var afterJobDTO = JobDTO.FromObservable(ActiveJob); _actionLogManager.InsertLog(ActionLogType.JobSaved, AuthenticationProvider.CurrentUser, _beforeSaveJobDTO.Name, _beforeSaveJobDTO, afterJobDTO, "Job saved from research module in Machine Studio."); _beforeSaveJobDTO = afterJobDTO; _machineDbContext.Entry(SelectedMachineJob).Reload(); _machineDbContext.Entry(SelectedMachineJob).Collection(x => x.Segments).Load(); foreach (var segment in SelectedMachineJob.Segments.ToList()) { _machineDbContext.Entry(segment).Collection(x => x.BrushStops).Load(); foreach (var brushStop in segment.BrushStops.ToList()) { _machineDbContext.Entry(brushStop).Reload(); } _machineDbContext.Entry(segment).Reload(); } InvokeUI(() => { SelectedMachineJob.Segments = SelectedMachineJob.Segments; }); SelectedMachineJob.RaisePropertyChanged(x => x.Length); SelectedMachineJob.RaisePropertyChanged(x => x.EstimatedDurationMili); var settings = SettingsManager.Default.GetOrCreate(); settings.DefaultJobRmlGuid = ActiveJob.RmlGuid; settings.Save(); if (ActiveJob != null) { _current_job_string = ActiveJob.ToJobFileWhenLoaded().ToString(); } }); } } catch (Exception ex) { LogManager.Log(ex, "Error saving active job."); _notification.ShowError($"An error occurred while trying to save the current job.\n{ex.FlattenMessage()}"); } finally { CanWork = true; } } } private void BackToJobs() { LogManager.Log("User request for 'back to jobs'..."); LogManager.Log("Comparing active job with selected job..."); bool jobModified = ActiveJob.ToJobFileWhenLoaded().ToString() != _current_job_string; if (jobModified) { LogManager.Log("Selected job has been modified. Invoking confirmation dialog..."); if (_notification.ShowQuestion("This will discard the current job changes. Are you sure?")) { LogManager.Log("Disposing active job db context..."); _activeJobDbContext.Dispose(); _navigation.NavigateTo(DeveloperNavigationView.MachineJobSelectionView); } } else { LogManager.Log("Disposing active job db context..."); _activeJobDbContext.Dispose(); _navigation.NavigateTo(DeveloperNavigationView.MachineJobSelectionView); } } #endregion #region Private Methods private async void LoadMachine() { try { LogManager.Log("Loading selected machine..."); CanWork = false; using (_notification.PushTaskItem("Loading selected machine...")) { await _machineDbContext.Jobs.Where(x => x.MachineGuid == SelectedMachine.Guid).Include(x => x.User).Include(x => x.User.Contact).LoadAsync(); foreach (var job in SelectedMachine.Jobs) { await job.Reload(_machineDbContext); } await _machineDbContext.ColorSpaces.LoadAsync(); await Task.Factory.StartNew(() => { _machineDbContext.Adapter.GetConfiguration(x => x.Guid == SelectedMachine.ConfigurationGuid); }); RaisePropertyChanged(nameof(SelectedMachine)); JobsCollectionView = CollectionViewSource.GetDefaultView(SelectedMachine.Jobs); JobsCollectionView.SortDescriptions.Add(new SortDescription(nameof(Job.LastUpdated), ListSortDirection.Descending)); } CanWork = true; foreach (var job in SelectedMachine.Jobs.OrderByDescending(x => x.LastUpdated)) { if (!CanWork) break; job.Segments.EnableCrossThreadOperations(); await _machineDbContext.Segments.Where(x => x.JobGuid == job.Guid).Include(x => x.BrushStops).OrderBy(x => x.SegmentIndex).LoadAsync(); } } catch (Exception ex) { LogManager.Log(ex); _notification.ShowError("An error occurred while trying to load the selected machine."); CanWork = true; } } private void UpdateEstimatedDuration() { if (ActiveJob != null && SelectedProcessParametersTable != null && SelectedProcessParametersTable.DyeingSpeed > 0) { EstimatedDuration = ActiveJob.GetEstimatedDuration(SelectedProcessParametersTable); } } private void SetSegmentBrushStopsLiquidVolumes(Segment segment) { if (!DesignMode && segment != null) { LogManager.Log("Setting segment brush stops liquid volumes..."); foreach (var stop in segment.BrushStops) { stop.SetLiquidVolumes(ActiveJob.Machine.Configuration, SelectedRML, SelectedProcessParametersTable); } } } /// /// Navigates to the Machine Designer Module in order to edit the selected machine. /// private void EditMachine() { LogManager.Log(String.Format("Requesting machine designer module for machine {0} editing...", SelectedMachine.SerialNumber)); } #endregion #region Add / Remove / Duplicate Jobs, Segments & Brush Stops /// /// Arranges the segments indices. /// private void ArrangeSegmentsIndices() { int index = 1; foreach (var segment in ActiveJob.Segments.OrderBy(x => x.SegmentIndex)) { segment.SegmentIndex = index++; } SegmentsCollectionView.Refresh(); } /// /// Arranges the brush stops indices. /// private void ArrangeBrushStopsIndices() { int index = 0; foreach (var stop in SelectedSegment.BrushStops.OrderBy(x => x.StopIndex)) { stop.SetStopIndexNoRaise(index++); } if (SelectedSegment.BrushStops.Count > 1) { SelectedSegment.BrushStops.OrderBy(x => x.StopIndex).First().OffsetPercent = 0; SelectedSegment.BrushStops.OrderBy(x => x.StopIndex).Last().OffsetPercent = 100; } foreach (var stop in SelectedSegment.BrushStops.OrderBy(x => x.StopIndex)) { stop.RaiseStopIndex(); stop.RaiseOffsetChanged(); } BrushStopsCollectionView.Refresh(); } /// /// Removes the selected segments. /// private void RemoveSelectedSegments() { if (ActiveJob != null && SelectedSegment != null) { if (_notification.ShowQuestion("Are you sure you want to delete the selected segments?")) { LogManager.Log(String.Format("Removing {0} segments...", SelectedSegments.Count)); SelectedSegments.ToList().ForEach(x => { if (ActiveJob.Segments.Count == 1) { _notification.ShowInfo("A job must contain at least one segment."); return; } x.Delete(_activeJobDbContext); }); ArrangeSegmentsIndices(); } } } /// /// Adds a new segment. /// private void AddSegment() { if (ActiveJob != null) { LogManager.Log($"Adding new segment to job {ActiveJob.Name}..."); Segment seg = new Segment(); seg.Job = ActiveJob; seg.Name = "SEGMENT"; seg.Length = 10; if (ActiveJob.Segments.Count > 0) { seg.SegmentIndex = ActiveJob.Segments.Max(x => x.SegmentIndex) + 1; } else { seg.SegmentIndex = 1; } ActiveJob.Segments.Add(seg); SelectedSegment = seg; AddBrushStop(); SetSegmentBrushStopsLiquidVolumes(SelectedSegment); ArrangeSegmentsIndices(); } } /// /// Removes the selected jobs. /// private async void RemoveSelectedJobs() { if (SelectedMachine != null && SelectedMachineJob != null) { if (_notification.ShowQuestion("Are you sure you want to delete the selected jobs?")) { try { IsFree = false; var jobsToReport = SelectedJobs.Select(x => JobDTO.FromObservable(x)).ToList(); LogManager.Log(String.Format("Removing {0} jobs...", SelectedJobs.Count)); SelectedJobs.ToList().ForEach(x => { x.Delete(_machineDbContext); }); using (_notification.PushTaskItem("Removing selected jobs...")) { LogManager.Log("Saving selected machine to database..."); await SelectedMachine.SaveAsync(_machineDbContext); } foreach (var job in jobsToReport) { _actionLogManager.InsertLog(ActionLogType.JobDeleted, AuthenticationProvider.CurrentUser, job.Name, job, "Job deleted using Machine Studio.", true); } } catch (Exception ex) { LogManager.Log(ex, "Error removing the selected job."); _notification.ShowError($"Error removing the selected job.\n{ex.FlattenMessage()}"); } finally { IsFree = true; } } } } /// /// Adds a new job to the selected machine. /// private async void AddJob() { if (SelectedMachine != null) { String jobName = _notification.ShowTextInput("Please provide a job name", "Name"); if (!String.IsNullOrWhiteSpace(jobName)) { LogManager.Log(String.Format("Adding new job {0}...", jobName)); var settings = SettingsManager.Default.GetOrCreate(); Job newJob = new Job(); newJob.LastUpdated = DateTime.UtcNow; newJob.JobSource = JobSource.Remote; newJob.Name = jobName; newJob.CreationDate = DateTime.UtcNow; newJob.UserGuid = AuthenticationProvider.CurrentUser.Guid; if (String.IsNullOrWhiteSpace(settings.DefaultJobRmlGuid)) { newJob.Rml = _machineDbContext.Rmls.FirstOrDefault(); } else { var rml = _machineDbContext.Rmls.SingleOrDefault(x => x.Guid == settings.DefaultJobRmlGuid); if (rml != null) { newJob.Rml = rml; } else { newJob.Rml = _machineDbContext.Rmls.FirstOrDefault(); } } newJob.WindingMethod = _machineDbContext.WindingMethods.FirstOrDefault(); newJob.SpoolType = _machineDbContext.SpoolTypes.FirstOrDefault(x => x.Code == (int)BL.Enumerations.SpoolTypes.StandardSpool); newJob.ColorSpace = _machineDbContext.ColorSpaces.FirstOrDefault(); newJob.Machine = SelectedMachine; SelectedMachine.Jobs.Add(newJob); var segment = newJob.AddSolidSegment(); segment.BrushStops[0].SetAllDispensingStepDivisions(BL.Dispensing.DispenserStepDivisions.D8); LogManager.Log("Saving selected machine to database..."); await SelectedMachine.SaveAsync(_machineDbContext); _actionLogManager.InsertLog(ActionLogType.JobCreated, AuthenticationProvider.CurrentUser, newJob.Name, newJob, "Job created using Machine Studio."); SelectedMachineJob = newJob; LoadSelectedJob(); } } } /// /// Removes the selected brush stop. /// private void RemoveSelectedBrushStops() { if (SelectedBrushStop != null && SelectedSegment != null) { if (_notification.ShowQuestion("Are you sure you want to delete the selected brush stops?")) { LogManager.Log(String.Format("Removing {0} brush stops...", SelectedBrushStops.Count)); SelectedBrushStops.ToList().ForEach(x => { if (SelectedSegment.BrushStops.Count == 1) { _notification.ShowInfo("A job segment must contain at least one brush stop."); return; } SelectedSegment.BrushStops.Remove(x); var existingBrushStop = _activeJobDbContext.BrushStops.FirstOrDefault(y => y.Guid == x.Guid); if (existingBrushStop != null) { _activeJobDbContext.BrushStops.Remove(existingBrushStop); } }); ArrangeBrushStopsIndices(); } } } /// /// Adds a new brush stop to the selected segment. /// private void AddBrushStop() { if (SelectedSegment != null) { LogManager.Log($"Adding new brush stop to segment '{SelectedSegment.SegmentIndex}'..."); var stop = new BrushStop(); if (SelectedSegment.BrushStops.Count > 0) { stop.StopIndex = SelectedSegment.BrushStops.Max(x => x.StopIndex) + 1; } else { stop.StopIndex = 1; } stop.OffsetPercent = 100; stop.Segment = SelectedSegment; stop.ColorSpace = ColorSpaces.FirstOrDefault(); stop.Color = Colors.Black; stop.SetAllDispensingStepDivisions(BL.Dispensing.DispenserStepDivisions.D8); stop.SetLiquidVolumes(SelectedMachine.Configuration, SelectedRML, SelectedProcessParametersTable); SelectedSegment.BrushStops.Add(stop); // _activeJobDbContext.BrushStops.Add(stop); SelectedSegment.BrushStops.ToList().ForEach(x => x.RaiseOffsetChanged()); ArrangeBrushStopsIndices(); } } /// /// Duplicates the selected brush stops. /// private void DuplicateSelectedBrushStops() { LogManager.Log($"Duplicating {SelectedBrushStops.Count} brush stops..."); foreach (var stop in SelectedBrushStops.OrderBy(x => x.StopIndex)) { var cloned = stop.Clone(); cloned.StopIndex = SelectedSegment.BrushStops.Max(x => x.StopIndex) + 1; cloned.SetLiquidVolumes(ActiveJob.Machine.Configuration, SelectedRML, SelectedProcessParametersTable); SelectedSegment.BrushStops.Add(cloned); } ArrangeBrushStopsIndices(); } /// /// Duplicates the selected segments. /// private void DuplicateSelectedSegments() { LogManager.Log($"Duplicating {SelectedSegments.Count} segments..."); int start_index = SelectedSegments.Max(x => x.SegmentIndex); ActiveJob.Segments.Where(x => x.SegmentIndex > start_index).ToList().ForEach(x => x.SegmentIndex = x.SegmentIndex + SelectedSegments.Count); foreach (var segment in SelectedSegments.OrderBy(x => x.SegmentIndex)) { var cloned = segment.Clone(); cloned.SegmentIndex = start_index++; ActiveJob.Segments.Add(cloned); SelectedSegment = cloned; } ArrangeSegmentsIndices(); } /// /// Duplicates the selected jobs. /// private async void DuplicateSelectedJobs() { if (SelectedMachineJob != null) { using (_notification.PushTaskItem("Cloning selected jobs...")) { CanWork = false; LogManager.Log($"Duplicating {SelectedJobs.Count} jobs..."); int index = SelectedMachine.Jobs.Max(x => x.JobIndex); foreach (var job in SelectedJobs) { var cloned = job.Clone(); cloned.JobIndex = ++index; SelectedMachine.Jobs.Add(cloned); } LogManager.Log("Saving selected machine to database..."); await SelectedMachine.SaveAsync(_machineDbContext); foreach (var job in SelectedJobs) { _actionLogManager.InsertLog(ActionLogType.JobCreated, AuthenticationProvider.CurrentUser, job.Name, job, "Job cloned using Machine Studio."); } CanWork = true; } } } #endregion #region Embroidery /// /// Imports embroidery file. /// private void ImportEmbroideryFile() { OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = "Select embroidery file"; dlg.Filter = EMB_FORMATS_IMPORT; if (dlg.ShowDialogCenter()) { var view = new EmbroideryImportView(); _notification.ShowModalDialog( new EmbroideryImportViewVM() { FileName = dlg.FileName }, view, (vm) => { String jobName = _notification.ShowTextInput("Please provide a job name", "Name"); if (jobName != null) { AddJobFromEmbroideryFile(jobName, vm, dlg.FileName, view.EmbroideryImageBytes); } }, () => { }); } } private async void AddJobFromEmbroideryFile(String jobName, EmbroideryImportViewVM vm, String fileName, byte[] imageBytes) { LogManager.Log(String.Format("Adding new job from embroidery file {0}...", jobName)); Job job = new Job(); job.Name = jobName; job.Name = jobName; job.CreationDate = DateTime.UtcNow; job.UserGuid = AuthenticationProvider.CurrentUser.Guid; job.Rml = _machineDbContext.Rmls.FirstOrDefault(); job.WindingMethod = _machineDbContext.WindingMethods.FirstOrDefault(); job.SpoolType = _machineDbContext.SpoolTypes.FirstOrDefault(); job.Machine = SelectedMachine; job.EmbroideryFileName = Path.GetFileName(fileName); job.EmbroideryFileData = File.ReadAllBytes(fileName); job.EmbroideryJpeg = imageBytes; job.HasEmbroideryFile = true; foreach (var path in vm.Paths.Skip(1)) { Segment segment = new Segment(); double baseLength = path.Length / 1000d; double embThicknessLength = (vm.EmbroideryMaterialThickness * path.StitchCount) / 1000d; double stabilizerThicknessLength = (vm.StabilizerThickness * path.StitchCount) / 1000d; double totalLength = (baseLength + embThicknessLength) * vm.SelectedEmbroideryMaterial.Coefficient; if (vm.HasStabilizer) { totalLength += (stabilizerThicknessLength * vm.SelectedStabilizer.Coefficient); } segment.Length = totalLength; segment.Name = "Embroidery Segment"; segment.SegmentIndex = vm.Paths.IndexOf(path); if (path.Brush is SolidColorBrush) { var brush = (path.Brush as SolidColorBrush); segment.BrushStops.Add(new BrushStop() { Red = brush.Color.R, Green = brush.Color.G, Blue = brush.Color.B, ColorSpace = _machineDbContext.ColorSpaces.ToList().SingleOrDefault(x => x.Code == BL.Enumerations.ColorSpaces.RGB.ToInt32()), }); } else { var brush = (path.Brush as LinearGradientBrush); foreach (var stop in brush.GradientStops) { segment.BrushStops.Add(new BrushStop() { StopIndex = brush.GradientStops.IndexOf(stop), Red = stop.Color.R, Green = stop.Color.G, Blue = stop.Color.B, OffsetPercent = stop.Offset * 100d, ColorSpace = _machineDbContext.ColorSpaces.ToList().SingleOrDefault(x => x.Code == BL.Enumerations.ColorSpaces.RGB.ToInt32()), }); } } job.Segments.Add(segment); } SelectedMachine.Jobs.Add(job); LogManager.Log("Saving selected machine to database..."); await SelectedMachine.SaveAsync(_machineDbContext); SelectedMachineJob = job; LoadSelectedJob(); } private void DisplayJobEmbroideryFile(Job job) { _notification.ShowModalDialog(new EmbroideryDisplayViewVM(job), (vm) => { SaveFileDialog dlg = new SaveFileDialog(); dlg.Title = "Select embroidery file location and format"; dlg.Filter = EMB_FORMATS_EXPORT; dlg.FileName = job.EmbroideryFileName; if (dlg.ShowDialogCenter()) { try { var tempDir = TemporaryManager.CreateFolder(); String filePath = Path.Combine(tempDir.Path, job.EmbroideryFileName); File.WriteAllBytes(filePath, job.EmbroideryFileData); EmbroideryFileConverter.ConvertEmbroideryFile(filePath, dlg.FileName); _notification.ShowInfo("Embroidery file exported successfully."); } catch (Exception ex) { LogManager.Log(ex, "An error has occurred while trying to export the attached embroidery file."); _notification.ShowError("An error has occurred while trying to export the attached embroidery file."); } } }, () => { }); } #endregion #region Job Import/Export private async void ExportJobFile() { if (SelectedJobs != null && SelectedJobs.Count > 1) { CommonOpenFileDialog dlg = new CommonOpenFileDialog(); dlg.Title = "Select a folder to place all job files."; dlg.IsFolderPicker = true; if (dlg.ShowDialog() == CommonFileDialogResult.Ok) { foreach (var job in SelectedJobs) { using (_notification.PushTaskItem($"Exporting job '{job.Name}'...")) { try { LogManager.Log($"Exporting job file {job.Name}"); var jobFile = await job.ToJobFile(); File.WriteAllBytes(Path.Combine(dlg.FileName, job.Name + ".job"), jobFile.ToBytes()); } catch (Exception ex) { LogManager.Log(ex, "Error exporting job file."); _notification.ShowError($"An error occurred while trying to export job '{job.Name}'.\n{ex.FlattenMessage()}"); } } } } } else { SaveFileDialog dlg = new SaveFileDialog(); dlg.Title = "Export Job File"; dlg.Filter = "Twine Job Files|*.job"; dlg.DefaultExt = ".job"; dlg.FileName = SelectedMachineJob.Name; if (dlg.ShowDialog().Value) { using (_notification.PushTaskItem($"Exporting job '{SelectedMachineJob.Name}'...")) { try { LogManager.Log($"Exporting job file {SelectedMachineJob.Name}"); var jobFile = await SelectedMachineJob.ToJobFile(); File.WriteAllBytes(dlg.FileName, jobFile.ToBytes()); } catch (Exception ex) { LogManager.Log(ex, "Error exporting job file."); _notification.ShowError($"An error occurred while trying to export job '{SelectedMachineJob.Name}'.\n{ex.FlattenMessage()}"); } } } } } private async void ImportJobFile() { OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = "Import Job Files"; dlg.Filter = "Twine Job Files|*.job"; dlg.Multiselect = true; if (dlg.ShowDialog().Value) { using (_notification.PushTaskItem($"Importing job files...")) { try { IsFree = false; LogManager.Log($"Importing job files..."); List jobsToReport = new List(); foreach (var file in dlg.FileNames) { var bytes = File.ReadAllBytes(file); var jobFile = JobFile.Parser.ParseFrom(bytes); var job = await Job.FromJobFile(jobFile, SelectedMachine.Guid, AuthenticationProvider.CurrentUser.Guid); job.JobSource = JobSource.Remote; _machineDbContext.Jobs.Add(job); jobsToReport.Add(job); } await _machineDbContext.SaveChangesAsync(); foreach (var job in jobsToReport) { _actionLogManager.InsertLog(ActionLogType.JobImported, AuthenticationProvider.CurrentUser, job.Name, job, "Job imported using Machine Studio."); } IsFree = true; _notification.ShowInfo($"Jobs imported successfully."); } catch (Exception ex) { LogManager.Log(ex, "Error importing job file."); _notification.ShowError($"An error occurred while trying to import the selected job file.\n{ex.FlattenMessage()}"); } finally { IsFree = true; } } } } #endregion #region Override Methods protected override void RaisePropertyChangedAuto([CallerMemberName] string caller = null) { base.RaisePropertyChangedAuto(caller); if (!_blockInvalidateCommands) { InvalidateRelayCommands(); } } #endregion #region IStudioViewModel public override Task OnShutdownRequest() { if (IsJobRunning) { InvokeUI(() => { _notification.ShowWarning("Please stop the currently running job before closing the developer module."); }); return Task.FromResult(false); } return Task.FromResult(true); } public override void OnShuttingDown() { } #endregion } }