using Google.Protobuf;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using Tango.BL.Builders;
using Tango.BL.Enumerations;
using Tango.Core;
using Tango.Core.ExtensionMethods;
using Tango.Logging;
using Tango.PMR.Exports;
namespace Tango.BL.Entities
{
public partial class Job : JobBase
{
private double _lastLength;
#region Constructors
///
/// Initializes a new instance of the class.
///
public Job() : base()
{
}
///
/// Initializes a new instance of the class.
///
public Job(DateTime creationDate) : this()
{
CreationDate = creationDate;
}
#endregion
#region Events
///
/// Occurs when the job total segments length has changed.
///
public event EventHandler LengthChanged;
#endregion
#region Properties
///
/// Gets the total job segments length.
///
[NotMapped]
[JsonIgnore]
public double Length
{
get
{
_lastLength = GetLength();
return _lastLength;
}
}
///
/// Gets the total job segments length multiplied by number of units if it is an embroidery job.
///
[NotMapped]
[JsonIgnore]
public double LengthIncludingNumberOfUnits
{
get
{
_lastLength = GetLength();
var l = _lastLength * NumberOfUnits;
if (EnableInterSegment && NumberOfUnits > 1)
{
l += ((NumberOfUnits - 1) * InterSegmentLength);
}
return l;
}
}
///
/// Gets or sets the job property as enum instead of int.
///
[NotMapped]
[JsonIgnore]
public JobStatuses JobStatus
{
get { return (JobStatuses)Status; }
set { Status = value.ToInt32(); RaisePropertyChangedAuto(); }
}
///
/// Gets or sets the job property as enum instead of int.
///
[NotMapped]
[JsonIgnore]
public JobTypes JobType
{
get { return (JobTypes)Type; }
set { Type = value.ToInt32(); RaisePropertyChangedAuto(); }
}
///
/// Gets or sets the job property as a property.
///
[NotMapped]
[JsonIgnore]
public bool IsAllSegmentsPerSpool
{
get { return ((SpoolsDistributions)SpoolsDistribution) == SpoolsDistributions.AllSegments; }
set
{
SpoolsDistribution = value ? SpoolsDistributions.AllSegments.ToInt32() : SpoolsDistributions.SingleSegment.ToInt32();
RaisePropertyChangedAuto();
}
}
///
/// Gets or sets the effective segments.
///
[NotMapped]
[JsonIgnore]
public ObservableCollection EffectiveSegments
{
get
{
if (EnableInterSegment && IsAllSegmentsPerSpool)
{
int max = Segments.Max(x => x.SegmentIndex);
ObservableCollection effectiveSegments = new ObservableCollection();
foreach (var s in Segments.ToList().OrderBy(x => x.SegmentIndex))
{
effectiveSegments.Add(s);
if (s.SegmentIndex != max)
{
effectiveSegments.Add(CreateInterSegment(InterSegmentLength));
}
}
return effectiveSegments;
}
else
{
return Segments.OrderBy(x => x.SegmentIndex).ToObservableCollection();
}
}
}
///
/// Gets the ordered segments.
///
[NotMapped]
[JsonIgnore]
public ObservableCollection OrderedSegments
{
get
{
return Segments.OrderBy(x => x.SegmentIndex).ToObservableCollection();
}
}
///
/// Gets or sets the job fine tuning status.
///
[NotMapped]
[JsonIgnore]
public FineTuningStatuses JobFineTuningStatus
{
get { return (FineTuningStatuses)FineTuningStatus; }
set { FineTuningStatus = value.ToInt32(); RaisePropertyChangedAuto(); }
}
///
/// Gets or sets the job sample dye status.
///
[NotMapped]
[JsonIgnore]
public SampleDyeStatuses JobSampleDyeStatus
{
get { return (SampleDyeStatuses)SampleDyeStatus; }
set { SampleDyeStatus = value.ToInt32(); RaisePropertyChangedAuto(); }
}
private JobDesignations _designation;
///
/// Gets or sets the designation.
///
[NotMapped]
[JsonIgnore]
public JobDesignations Designation
{
get { return _designation; }
set { _designation = value; RaisePropertyChangedAuto(); }
}
///
/// Gets or sets the state of the job editing.
///
[NotMapped]
[JsonIgnore]
public EditingStates JobEditingState
{
get { return (EditingStates)EditingState; }
set { EditingState = value.ToInt32(); RaisePropertyChangedAuto(); }
}
#endregion
#region Event Handlers
///
/// Handles the CollectionChanged event of the Segments collection.
///
/// The source of the event.
/// The instance containing the event data.
private void Segments_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
foreach (var segment in Segments.ToList())
{
segment.PropertyChanged -= Segment_PropertyChanged;
segment.PropertyChanged += Segment_PropertyChanged;
}
OnLengthChanged();
RaisePropertyChanged(nameof(EffectiveSegments));
}
///
/// Handles the PropertyChanged event of all job segments.
///
/// The source of the event.
/// The instance containing the event data.
private void Segment_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Segment.Length))
{
OnLengthChanged();
}
else if (e.PropertyName == nameof(Segment.SegmentIndex))
{
RaisePropertyChanged(nameof(EffectiveSegments));
}
}
#endregion
#region Virtual Methods
///
/// Called when the property has been changed
///
protected virtual void OnLengthChanged()
{
if (_lastLength != GetLength())
{
RaisePropertyChanged(nameof(Length));
RaisePropertyChanged(nameof(LengthIncludingNumberOfUnits));
LengthChanged?.Invoke(this, new EventArgs());
}
}
#endregion
#region Override Methods
///
/// Raises the property changed event.
///
/// Name of the property.
protected override void RaisePropertyChanged(string propName)
{
base.RaisePropertyChanged(propName);
if (propName == nameof(Segments))
{
if (Segments != null)
{
Segments.CollectionChanged -= Segments_CollectionChanged;
Segments.CollectionChanged += Segments_CollectionChanged;
OnLengthChanged();
RaisePropertyChanged(nameof(EffectiveSegments));
}
}
if (propName == nameof(InterSegmentLength) || propName == nameof(EnableInterSegment) || propName == nameof(SpoolsDistribution))
{
OnLengthChanged();
RaisePropertyChanged(nameof(EffectiveSegments));
}
if (propName == nameof(ColorSpace))
{
//Make all brush stops the same color space if job color space is not null!
if (ColorSpace != null)
{
Segments.SelectMany(x => x.BrushStops).ToList().ForEach(x => x.ColorSpace = ColorSpace);
}
}
if (propName == nameof(NumberOfUnits))
{
LengthChanged?.Invoke(this, new EventArgs());
}
if (propName == nameof(LengthPercentageFactor))
{
OnLengthChanged();
Segments.ToList().ForEach(x => x.RaiseLengthWithFactorChanged());
}
if (InterSegmentLength < 1)
{
InterSegmentLength = 1;
}
}
public override Job Clone()
{
Job cloned = base.Clone();
cloned.Name = Name + " - Copy";
cloned.CreationDate = DateTime.UtcNow;
cloned.LastRun = null;
cloned.ColorSpace = ColorSpace;
cloned.Customer = Customer;
cloned.Rml = Rml;
cloned.SpoolType = SpoolType;
cloned.WindingMethod = WindingMethod;
cloned.JobStatus = JobStatuses.Draft;
cloned.Segments = Segments.Select(x => x.Clone(cloned)).ToSynchronizedObservableCollection();
foreach (var segment in cloned.Segments)
{
segment.JobGuid = cloned.Guid;
segment.Job = cloned;
}
return cloned;
}
#endregion
#region Private Methods
private double GetLength()
{
if (Segments != null)
{
return Segments.Sum(x => x.LengthWithFactor) + ((EnableInterSegment && IsAllSegmentsPerSpool) ? (InterSegmentLength * (Segments.Count > 0 ? Segments.Count - 1 : Segments.Count)) : 0);
}
else
{
return 0;
}
}
#endregion
#region Public Methods
public BitmapSource CreateSegmentsPie(double width, double height)
{
Bitmap bmp = new Bitmap((int)width, (int)height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Transparent);
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
int fromAngle = -90;
double totalLength = Segments.Sum(x => x.Length); //Excluding inter segment.
foreach (var segment in OrderedSegments)
{
int toAngle = (int)((segment.Length / totalLength) * 360d);
Rectangle rect = new Rectangle(0, 0, bmp.Width - 2, bmp.Height - 2);
g.FillPie(segment.CreateGdiBrush(bmp.Width - 2, bmp.Height - 2), rect, fromAngle, toAngle);
Pen pen = new Pen(Color.Gainsboro);
g.DrawEllipse(pen, rect);
pen.Dispose();
fromAngle += toAngle;
}
}
var source = bmp.ToBitmapSource();
bmp.Dispose();
return source;
}
///
/// Adds a new solid segment.
///
public Segment AddSolidSegment()
{
return AddSolidSegment(System.Windows.Media.Colors.Black);
}
///
/// Adds a new solid segment.
///
public Segment AddSolidSegment(System.Windows.Media.Color color)
{
return AddSolidSegment(color, 10);
}
///
/// Adds a new solid segment.
///
public Segment AddSolidSegment(double length)
{
return AddSolidSegment(System.Windows.Media.Colors.White, length);
}
///
/// Adds a new solid segment.
///
public Segment AddSolidSegment(System.Windows.Media.Color color, double length)
{
Segment segment = new Segment();
segment.Name = "Standard Segment";
if (Segments.Count > 0)
{
segment.SegmentIndex = Segments.Max(x => x.SegmentIndex) + 1;
}
else
{
segment.SegmentIndex = 1;
}
segment.Length = length;
segment.Job = this;
var stop = segment.AddBrushStop();
stop.Color = color;
Segments.Add(segment);
return segment;
}
///
/// Adds a new gradient segment.
///
public Segment AddGradientSegment()
{
return AddGradientSegment(10);
}
///
/// Adds a new gradient segment.
///
public Segment AddGradientSegment(double length)
{
var segment = AddSolidSegment(length);
segment.BrushStops.Last().Color = System.Windows.Media.Colors.White;
segment.AddBrushStop();
segment.BrushStops.Last().Color = System.Windows.Media.Colors.White;
return segment;
}
///
/// Gets the duration estimation for this job.
///
/// The process parameters.
///
public TimeSpan GetEstimatedDuration(ProcessParametersTable processParameters)
{
if (processParameters.DyeingSpeed == 0)
{
throw new ArgumentException("Process parameters dying speed cannot be zero.");
}
return TimeSpan.FromSeconds((LengthIncludingNumberOfUnits + processParameters.DryerBufferLengthMeters) / (processParameters.DyeingSpeed / 100d));
}
/////
///// Gets the duration estimation for this job.
/////
/////
//public Task GetEstimatedDuration()
//{
// return Task.Factory.StartNew(() =>
// {
// var process = GetRecommendedProcessParameters().Result;
// return GetEstimatedDuration(process);
// });
//}
///
/// Translates the job progress to time.
///
/// The progress.
/// The process parameters.
///
public static TimeSpan TranslateProgressToTime(double progress, ProcessParametersTable processParameters)
{
return TimeSpan.FromSeconds(progress / (processParameters.DyeingSpeed / 100d));
}
///
/// Creates an inter segment.
///
/// The length.
///
public static Segment CreateInterSegment(double length)
{
return new Segment()
{
IsInterSegment = true,
Length = length,
Name = "Inter Segment",
BrushStops = new SynchronizedObservableCollection()
{
new BrushStop()
{
ColorSpace = new ColorSpace(),
Color = System.Windows.Media.Colors.White,
}
},
};
}
//public Task GetRecommendedProcessParameters()
//{
// return Task.Factory.StartNew(() =>
// {
// try
// {
// int index = ColorConversion.TangoColorConverter.GetLeastCommonProcessParametersTableIndex(Segments.SelectMany(x => x.BrushStops));
// if (index < Rml.GetActiveProcessGroup().ProcessParametersTables.Count)
// {
// return Rml.GetActiveProcessGroup().ProcessParametersTables[index];
// }
// else
// {
// return Rml.GetActiveProcessGroup().ProcessParametersTables[0];
// }
// }
// catch (Exception ex)
// {
// throw new InvalidOperationException("Could not calculate the recommended process parameters for the job.", ex);
// }
// });
//}
///
/// Creates a PMR job file from the this job based on its database data.
///
///
public Task ToJobFile()
{
return Task.Factory.StartNew(() =>
{
using (ObservablesContext db = ObservablesContext.CreateDefault())
{
var job = new JobBuilder(db).Set(Guid).WithUser().WithRML().WithSegments().WithBrushStops().Build();
var machine = new MachineBuilder(db).Set(job.MachineGuid).WithConfiguration().Build();
return CreateJobFile(job, machine);
}
});
}
///
/// Creates a PMR job file based on the current state of the object.
///
///
public JobFile ToJobFileWhenLoaded()
{
return CreateJobFile(this, Machine);
}
private static JobFile CreateJobFile(Job job, Machine machine)
{
var jobFile = new JobFile();
jobFile.ColorSpaceGuid = job.ColorSpaceGuid.ToStringOrEmpty();
jobFile.Customer = job.Customer != null ? job.Customer.Name : String.Empty;
jobFile.Description = job.Description.ToStringOrEmpty();
if (job.HasEmbroideryFile)
{
jobFile.HasEmbroideryFile = job.HasEmbroideryFile;
jobFile.EmbroideryFileData = ByteString.CopyFrom(job.EmbroideryFileData);
jobFile.EmbroideryFileName = job.EmbroideryFileName;
jobFile.EmbroideryJpeg = ByteString.CopyFrom(job.EmbroideryJpeg);
}
jobFile.EnableInterSegment = job.EnableInterSegment;
jobFile.EnableLubrication = job.EnableLubrication;
jobFile.InterSegmentLength = job.InterSegmentLength;
jobFile.LengthPercentageFactor = job.LengthPercentageFactor;
jobFile.Name = job.Name.ToStringOrEmpty();
jobFile.NumberOfUnits = job.NumberOfUnits;
jobFile.RmlGuid = job.RmlGuid;
jobFile.SampleUnitsOrMeters = job.SampleUnitsOrMeters;
jobFile.SpoolsDistribution = job.SpoolsDistribution;
jobFile.SpoolTypeGuid = job.SpoolTypeGuid;
jobFile.Type = job.Type;
jobFile.WindingMethodGuid = job.WindingMethodGuid;
jobFile.ColorCatalogGuid = job.ColorCatalogGuid.ToStringOrEmpty();
foreach (var segment in job.OrderedSegments)
{
JobFileSegment s = new JobFileSegment();
s.Length = segment.Length;
s.Name = segment.Name.ToStringOrEmpty();
jobFile.Segments.Add(s);
foreach (var stop in segment.BrushStops.OrderBy(x => x.StopIndex))
{
JobFileBrushStop st = new JobFileBrushStop();
stop.MapPrimitivesWithStringsNoNullsTo(st);
st.ColorCatalogItemGuid = stop.ColorCatalogsItemGuid.ToStringOrEmpty();
foreach (var idsPack in machine.Configuration.NoneEmptyIdsPacks)
{
try
{
var volume = stop.GetVolume(idsPack.PackIndex);
st.LiquidVolumes.Add(new JobFileLiquidVolume()
{
LiquidTypeName = idsPack.LiquidType.Name,
Volume = volume,
});
}
catch (Exception ex)
{
throw new InvalidOperationException($"Error extracting liquid volume from ids pack {idsPack.PackIndex}", ex);
}
}
s.BrushStops.Add(st);
}
}
return jobFile;
}
public static Task FromJobFile(JobFile jobFile, String machineGuid, String userGuid)
{
return Task.Factory.StartNew(() =>
{
using (ObservablesContext db = ObservablesContext.CreateDefault())
{
Machine machine;
var job = new Job();
job.MachineGuid = machineGuid;
job.UserGuid = userGuid;
job.CreationDate = DateTime.UtcNow;
try
{
machine = new MachineBuilder(db).Set(machineGuid).WithConfiguration().Build();
}
catch
{
throw new ArgumentException("Error loading the machine details.");
}
if (!String.IsNullOrEmpty(jobFile.ColorSpaceGuid))
{
var job_color_space = db.ColorSpaces.SingleOrDefault(x => x.Guid == jobFile.ColorSpaceGuid);
if (job_color_space == null) throw new ArgumentException("Could not load the specified job file. Job color space could not be located on database.");
job.ColorSpaceGuid = jobFile.ColorSpaceGuid;
}
if (!String.IsNullOrEmpty(jobFile.ColorCatalogGuid))
{
var job_color_catalog = db.ColorCatalogs.SingleOrDefault(x => x.Guid == jobFile.ColorCatalogGuid);
if (job_color_catalog == null) throw new ArgumentException("Could not load the specified job file. Job color catalog could not be located on database.");
job.ColorCatalogGuid = jobFile.ColorCatalogGuid;
}
var job_customer = db.Customers.FirstOrDefault(x => x.Name == jobFile.Customer);
if (job_customer != null)
{
job.CustomerGuid = job_customer.Guid;
}
job.Description = jobFile.Description.ToNullIfEmpty();
if (jobFile.HasEmbroideryFile)
{
job.HasEmbroideryFile = jobFile.HasEmbroideryFile;
job.EmbroideryFileData = jobFile.EmbroideryFileData.ToByteArray();
job.EmbroideryFileName = jobFile.EmbroideryFileName;
job.EmbroideryJpeg = jobFile.EmbroideryJpeg.ToByteArray();
}
job.EnableInterSegment = jobFile.EnableInterSegment;
job.EnableLubrication = jobFile.EnableLubrication;
job.InterSegmentLength = jobFile.InterSegmentLength;
job.LengthPercentageFactor = jobFile.LengthPercentageFactor;
job.Name = jobFile.Name.ToNullIfEmpty();
job.NumberOfUnits = jobFile.NumberOfUnits;
var job_rml = db.Rmls.SingleOrDefault(x => x.Guid == jobFile.RmlGuid);
if (job_rml == null) throw new ArgumentException("Could not load the specified job file. Job media type could not be located on database.");
job.RmlGuid = jobFile.RmlGuid;
job.SampleUnitsOrMeters = jobFile.SampleUnitsOrMeters;
job.SpoolsDistribution = jobFile.SpoolsDistribution;
var job_spool_type = db.SpoolTypes.SingleOrDefault(x => x.Guid == jobFile.SpoolTypeGuid);
if (job_spool_type == null) throw new ArgumentException("Could not load the specified job file. Job spool type could not be located on database.");
job.SpoolTypeGuid = jobFile.SpoolTypeGuid;
job.Type = jobFile.Type;
var job_winding_method = db.WindingMethods.Single(x => x.Guid == jobFile.WindingMethodGuid);
if (job_winding_method == null) throw new ArgumentException("Could not load the specified job file. Job winding method could not be located on database.");
job.WindingMethodGuid = jobFile.WindingMethodGuid;
for (int i = 0; i < jobFile.Segments.Count; i++)
{
var segment = jobFile.Segments[i];
Segment s = new Segment();
s.JobGuid = job.Guid;
s.Name = segment.Name.ToNullIfEmpty();
s.Length = segment.Length;
s.SegmentIndex = i + 1;
job.Segments.Add(s);
for (int j = 0; j < segment.BrushStops.Count; j++)
{
var stop = segment.BrushStops[j];
var stop_color_space = db.ColorSpaces.SingleOrDefault(x => x.Guid == stop.ColorSpaceGuid);
if (stop_color_space == null) throw new ArgumentException("Could not load the specified job file. Job brush stop color space could not be located on database.");
BrushStop st = new BrushStop();
st.StopIndex = j + 1;
st.SegmentGuid = s.Guid;
stop.MapPrimitivesWithStringsNoNullsTo(st);
foreach (var volume in stop.LiquidVolumes)
{
var idsPack = machine.Configuration.NoneEmptyIdsPacks.SingleOrDefault(x => x.LiquidType.Name == volume.LiquidTypeName);
if (idsPack == null)
{
throw new InvalidOperationException($"Liquid type '{volume.LiquidTypeName}' is not configured on the target machine.");
}
st.SetVolume(idsPack.PackIndex, volume.Volume);
}
if (!String.IsNullOrWhiteSpace(stop.ColorCatalogGuid))
{
var stop_color_catalog = db.ColorCatalogs.SingleOrDefault(x => x.Guid == stop.ColorCatalogGuid);
if (stop_color_catalog == null) throw new ArgumentException("Could not load the specified job file. Job brush stop color catalog could not be located on database.");
stop.ColorCatalogGuid = stop_color_catalog.Guid;
}
if (!String.IsNullOrWhiteSpace(stop.ColorCatalogItemGuid))
{
var stop_color_catalog_item = db.ColorCatalogsItems.SingleOrDefault(x => x.Guid == stop.ColorCatalogItemGuid);
if (stop_color_catalog_item == null) throw new ArgumentException("Could not load the specified job file. Job brush stop catalog color could not be located on database.");
st.ColorCatalogsItemGuid = stop.ColorCatalogItemGuid;
}
s.BrushStops.Add(st);
}
}
return job;
}
});
}
///
/// Removes this entity and all dependent entities from the specified db context.
///
/// The context.
public override void Delete(ObservablesContext context)
{
base.Delete(context);
Segments.ToList().ForEach(x => x.Delete(context));
context.Jobs.Remove(this);
}
#endregion
#region Validation
protected override void OnValidating(ObservablesContext context)
{
base.OnValidating(context);
if (String.IsNullOrWhiteSpace(Name))
{
InsertError(nameof(Name), "Job name is required");
}
}
#endregion
}
}