using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.BL;
using Tango.BL.Entities;
using Tango.BL.Enumerations;
using Tango.Core;
using Tango.Core.DI;
using Tango.Integration.Operation;
using Tango.PMR.MachineStatus;
using Tango.PPC.Common;
using Tango.PPC.Common.Build;
using Tango.PPC.Common.Connection;
using Tango.PPC.Common.Messages;
using Tango.PPC.Common.Models;
using Tango.PPC.Common.Navigation;
using Tango.PPC.Common.Notifications;
using Tango.PPC.Common.Printing;
using Tango.PPC.Jobs.Messages;
using Tango.PPC.UI.Dialogs;
using Tango.Settings;
namespace Tango.PPC.UI.Printing
{
///
/// Represents a printing manager capable of executing jobs, changing job statuses according to completion stage and generating sample and fine tuning jobs.
///
///
///
public class DefaultPrintingManager : ExtendedObject, IPrintingManager
{
private IMachineProvider _machineProvider;
private INotificationProvider _notificationProvider;
private PPCSettings _settings;
private IBuildProvider _buildProvider;
public bool PreventPrintingByRemoteDesktop { get; set; }
///
/// Initializes a new instance of the class.
///
/// The machine provider.
public DefaultPrintingManager(IMachineProvider machineProvider, INotificationProvider notificationProvider, IBuildProvider buildProvider)
{
_machineProvider = machineProvider;
_notificationProvider = notificationProvider;
_settings = SettingsManager.Default.GetOrCreate();
_buildProvider = buildProvider;
}
///
/// Prints the specified job.
/// When the job is complete the job status will change according to the completion stage.
///
/// The job.
/// The context.
///
public async Task Print(Job job, ObservablesContext context, PrintingConfiguration printConfig = null)
{
ThrowIfJobInvalid(job);
JobHandler handler = null;
#if STUBPRINT
handler = await _machineProvider.MachineOperator.PrintStub(job);
#else
try
{
if (PreventPrintingByRemoteDesktop)
{
await _notificationProvider.ShowError("You do not have permissions to perform job execution via remote desktop.");
throw new OperationCanceledException();
}
//Check for none volume gradients which has extra inks
foreach (var segment in job.Segments)
{
var hasExtraInks = _machineProvider.Machine.Configuration.IdsPacks.Any(x => x.LiquidType.IsExtraInk);
var hasNoneVolumeStops = segment.BrushStops.Any(x => x.BrushColorSpace != ColorSpaces.Volume);
var isGradient = segment.BrushStops.OrderBy(x => x.StopIndex).First().Color != segment.BrushStops.OrderBy(x => x.StopIndex).Last().Color;
if (hasExtraInks && hasNoneVolumeStops && isGradient)
{
await _notificationProvider.ShowError($"When dyeing a gradient and extra inks are installed, all colors must be defined in Volume color space.");
throw new OperationCanceledException();
}
}
//Spool Replace For TS 1800
if (!_buildProvider.IsEureka && job.Designation == JobDesignations.Default && _settings.EnableSpoolReplacementDialog && !_machineProvider.MachineOperator.IsSpoolReplaced)
{
if (!(await _notificationProvider.ShowDialog(new SpoolReplaceViewVM())).DialogResult)
{
throw new OperationCanceledException();
}
}
//Spool Replace For Eureka
if (_buildProvider.IsEureka && job.Designation == JobDesignations.Default && _settings.EnableSpoolReplacementDialog && printConfig != null && printConfig.GlobalStartPosition > 0)
{
if (!(await _notificationProvider.ShowDialog(new SpoolReplaceViewVM())).DialogResult)
{
throw new OperationCanceledException();
}
}
_notificationProvider.SetGlobalBusyMessage("Processing job...");
//Apply additional job configuration...
AdditionalJobConfiguration config = new AdditionalJobConfiguration();
config.UseColorConversion = true;
config.UseLightInks = true;
config.UseLubricantVolume = true;
var settings = SettingsManager.Default.GetOrCreate();
var rmlLubrication = settings.LubricationLevels.FirstOrDefault(x => x.RmlGuid == job.RmlGuid);
if (rmlLubrication != null)
{
config.LubricationVolume = (int)rmlLubrication.LubricationLevel;
}
var spoolTypeGuid = settings.SpoolTypeGuid;
var spoolType = await context.SpoolTypes.FirstOrDefaultAsync(x => x.Guid == spoolTypeGuid);
if (spoolType == null)
{
spoolType = await context.SpoolTypes.FirstOrDefaultAsync(x => x.Code == (int)SpoolTypes.StandardSpool);
}
if (job.SpoolType.Type == SpoolTypes.FlatSpool)
{
spoolType = await context.SpoolTypes.FirstOrDefaultAsync(x => x.Code == (int)SpoolTypes.FlatSpool);
}
job.SpoolType = spoolType;
if (printConfig != null && printConfig.GlobalStartPosition > 0)
{
config.ResumeConfig = new AdditionalJobConfiguration.ResumeConfiguration();
config.ResumeConfig.FirstUnitStartPosition = printConfig.FirstUnitStartPosition;
config.ResumeConfig.GlobalStartPosition = printConfig.GlobalStartPosition;
config.ResumeConfig.RemainingUnits = printConfig.RemainingUnits;
config.ResumeConfig.ResumeProgress = printConfig.ResumeProgress;
}
handler = await _machineProvider.MachineOperator.Print(job, config);
_notificationProvider.ReleaseGlobalBusyMessage();
}
catch (InsufficientLiquidQuantityException ex)
{
_notificationProvider.ReleaseGlobalBusyMessage();
LogManager.Log(ex);
var vm = await _notificationProvider.ShowDialog(new InsufficientLiquidQuantityViewVM(ex, _machineProvider.MachineOperator.MachineStatus.AutoInkFillingEnabled));
if (vm.StartAutoInkFillingOnExit)
{
try
{
_notificationProvider.SetGlobalBusyMessage("Starting ink filling...");
await _machineProvider.MachineOperator.SendRequest(new InitiateInkFillingRequest());
}
catch (Exception exx)
{
LogManager.Log(exx, "Error starting ink filling.");
await _notificationProvider.ShowError($"Error starting ink filling.\n{ex.Message}");
}
finally
{
_notificationProvider.ReleaseGlobalBusyMessage();
}
}
throw ex;
}
catch (Exception ex)
{
_notificationProvider.ReleaseGlobalBusyMessage();
LogManager.Log(ex);
throw ex;
}
#endif
handler.Completed += async (x, e) =>
{
if (job.Designation == JobDesignations.FineTuning) return;
try
{
job.JobStatus = JobStatuses.Completed;
if (!context.IsDisposed)
{
await context.SaveChangesAsync();
RaiseJobSaved(job);
}
else
{
using (var newContext = ObservablesContext.CreateDefault())
{
var newJob = newContext.Jobs.SingleOrDefault(y => y.Guid == job.Guid);
if (newJob != null)
{
newJob.JobStatus = JobStatuses.Completed;
await newContext.SaveChangesAsync();
RaiseJobSaved(job);
}
}
}
}
catch (Exception ex)
{
LogManager.Log(ex, "Error occurred after job printing completed.");
}
};
handler.Canceled += (x, e) =>
{
if (job.Designation == JobDesignations.FineTuning) return;
try
{
//No change in status !
//await context.SaveChangesAsync();
RaiseJobSaved(job);
}
catch (Exception ex)
{
LogManager.Log(ex, "Error occurred after job printing completed.");
}
};
handler.Failed += async (x, e) =>
{
if (job.Designation == JobDesignations.FineTuning) return;
try
{
job.JobStatus = JobStatuses.Disrupted;
if (!context.IsDisposed)
{
await context.SaveChangesAsync();
}
else
{
using (var newContext = ObservablesContext.CreateDefault())
{
var newJob = newContext.Jobs.SingleOrDefault(y => y.Guid == job.Guid);
if (newJob != null)
{
newJob.JobStatus = JobStatuses.Disrupted;
await newContext.SaveChangesAsync();
}
}
}
RaiseJobSaved(job);
}
catch (Exception ex)
{
LogManager.Log(ex, "Error occurred after job printing completed.");
}
};
return handler;
}
///
/// Creates a sample dye job from the specified job and prints it.
/// When the sample dye job is complete, the job sample dye status will change.
///
/// The job.
/// The context.
///
public async Task PrintSample(Job job, ObservablesContext context)
{
ThrowIfJobInvalid(job);
LogManager.Log("Cloning job...");
Job sampleDyeJob = job.Clone();
sampleDyeJob.Guid = job.Guid;
sampleDyeJob.Designation = BL.Enumerations.JobDesignations.SampleDye;
sampleDyeJob.Name = job.Name;
if (job.JobType == BL.Enumerations.JobTypes.Embroidery)
{
sampleDyeJob.NumberOfUnits = job.SampleUnitsOrMeters;
}
else
{
sampleDyeJob.NumberOfUnits = 1;
foreach (var segment in sampleDyeJob.OrderedSegments)
{
segment.Length = job.SampleUnitsOrMeters;
}
}
context.SaveChanges();
RaiseJobSaved(job);
LogManager.Log("Executing sample dye job...");
var handler = await _machineProvider.MachineOperator.Print(sampleDyeJob);
handler.Completed += async (x, e) =>
{
try
{
job.JobEditingState = BL.Enumerations.EditingStates.SampleDye;
job.JobSampleDyeStatus = BL.Enumerations.SampleDyeStatuses.PendingApproval;
await context.SaveChangesAsync();
}
catch (Exception ex)
{
LogManager.Log(ex, "Error occurred after sample dye printing completed.");
}
};
return handler;
}
///
/// Creates a fine tuning job from the specified job and fine tune items.
/// When the fine tuning job is complete, the job fine tuning status will change.
///
/// The job.
/// The context.
/// The fine tune items.
///
public async Task PrintFineTuning(Job job, ObservablesContext context, IEnumerable fineTuneItems)
{
ThrowIfJobInvalid(job);
LogManager.Log("Cloning job...");
Job fineTuneJob = job.Clone();
fineTuneJob.NumberOfUnits = 1;
fineTuneJob.Designation = BL.Enumerations.JobDesignations.FineTuning;
fineTuneJob.Guid = job.Guid;
fineTuneJob.Name = job.Name;
fineTuneJob.Segments.Clear();
foreach (var suggestion in fineTuneItems.Where(x => x.IsSelected).SelectMany(x => x.Suggestions))
{
var segment = fineTuneJob.AddSolidSegment(suggestion.Color);
}
var handler = await _machineProvider.MachineOperator.Print(fineTuneJob);
handler.Completed += async (x, e) =>
{
try
{
job.JobEditingState = BL.Enumerations.EditingStates.FineTuning;
job.JobFineTuningStatus = BL.Enumerations.FineTuningStatuses.PendingApproval;
await context.SaveChangesAsync();
}
catch (Exception ex)
{
LogManager.Log(ex, "Error occurred after fine tunning job completed.");
}
};
return handler;
}
///
/// Raises the job saved messenger message.
///
/// The job.
private void RaiseJobSaved(Job job)
{
TangoMessenger.Default.Send(new JobSavedMessage() { Job = job });
}
private void ThrowIfJobInvalid(Job job)
{
if (job.Segments.SelectMany(x => x.BrushStops).Any(x => x.IsOutOfGamut))
{
throw new InvalidOperationException("Error starting job. Color is out of range.");
}
if (job.Segments.SelectMany(x => x.BrushStops).Any(x => x.IsLiquidVolumesOutOfRange))
{
throw new InvalidOperationException($"The specified ink volumes at segment {job.Segments.SelectMany(x => x.BrushStops).First(x => x.IsLiquidVolumesOutOfRange).Segment.SegmentIndex} exceeds the maximum allowed total volume for the current thread.");
}
if (job.Segments.SelectMany(x => x.BrushStops).Any(x => x.BrushColorSpace == ColorSpaces.Catalog && x.ColorCatalogsItem == null && !x.IsTransparent && !x.IsWhite))
{
throw new InvalidOperationException("Error starting job. Please select a catalog color.");
}
if (job.Rml.Cct == null)
{
throw new InvalidOperationException($"Error starting job. No color table found for thread '{job.Rml.Name}'.");
}
}
}
}