aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.DB/CustomAttributes/DBViewAttribute.cs
blob: d512ae460c20e9a607d2281163714f14bcc383b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Tango.MachineStudio.DB.CustomAttributes
{
    /// <summary>
    /// Represents a database view attribute. This will tell the module to treat the view as a data table view.
    /// </summary>
    /// <seealso cref="System.Attribute" />
    public class DBViewAttribute : Attribute
    {
    }
}
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.Logging;
using Tango.PMR.Exports;

namespace Tango.BL.Entities
{
    public partial class Job : JobBase
    {
        private double _lastLength;

        /// <summary>
        /// Initializes a new instance of the <see cref="Job"/> class.
        /// </summary>
        public Job(DateTime creationDate)
        {
            CreationDate = creationDate;
        }

        #region Events

        /// <summary>
        /// Occurs when the job total segments length has changed.
        /// </summary>
        public event EventHandler LengthChanged;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the total job segments length.
        /// </summary>
        [NotMapped]
        [JsonIgnore]
        public double Length
        {
            get
            {
                _lastLength = GetLength();
                return _lastLength;
            }
        }

        /// <summary>
        /// Gets the total job segments length multiplied by number of units if it is an embroidery job.
        /// </summary>
        [NotMapped]
        [JsonIgnore]
        public double LengthIncludingNumberOfUnits
        {
            get
            {
                _lastLength = GetLength();
                var l = _lastLength * NumberOfUnits;

                if (EnableInterSegment && NumberOfUnits > 1)
                {
                    l += ((NumberOfUnits - 1) * InterSegmentLength);
                }

                return l;
            }
        }

        /// <summary>
        /// Gets or sets the job <see cref="Status"/> property as <see cref="JobStatus"/> enum instead of int.
        /// </summary>
        [NotMapped]
        [JsonIgnore]
        public JobStatuses JobStatus
        {
            get { return (JobStatuses)Status; }
            set { Status = value.ToInt32(); RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Gets or sets the job <see cref="Type"/> property as <see cref="JobType"/> enum instead of int.
        /// </summary>
        [NotMapped]
        [JsonIgnore]
        public JobTypes JobType
        {
            get { return (JobTypes)Type; }
            set { Type = value.ToInt32(); RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Gets or sets the job <see cref="SpoolsDistribution"/> property as a <see cref="Boolean"/> property.
        /// </summary>
        [NotMapped]
        [JsonIgnore]
        public bool IsAllSegmentsPerSpool
        {
            get { return ((SpoolsDistributions)SpoolsDistribution) == SpoolsDistributions.AllSegments; }
            set
            {
                SpoolsDistribution = value ? SpoolsDistributions.AllSegments.ToInt32() : SpoolsDistributions.SingleSegment.ToInt32();
                RaisePropertyChangedAuto();
            }
        }

        /// <summary>
        /// Gets or sets the effective segments.
        /// </summary>
        [NotMapped]
        [JsonIgnore]
        public ObservableCollection<Segment> EffectiveSegments
        {
            get
            {
                if (EnableInterSegment && IsAllSegmentsPerSpool)
                {
                    int max = Segments.Max(x => x.SegmentIndex);

                    ObservableCollection<Segment> effectiveSegments = new ObservableCollection<Segment>();

                    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();
                }
            }
        }

        /// <summary>
        /// Gets the ordered segments.
        /// </summary>
        [NotMapped]
        [JsonIgnore]
        public ObservableCollection<Segment> OrderedSegments
        {
            get
            {
                return Segments.OrderBy(x => x.SegmentIndex).ToObservableCollection();
            }
        }

        /// <summary>
        /// Gets or sets the job fine tuning status.
        /// </summary>
        [NotMapped]
        [JsonIgnore]
        public FineTuningStatuses JobFineTuningStatus
        {
            get { return (FineTuningStatuses)FineTuningStatus; }
            set { FineTuningStatus = value.ToInt32(); RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Gets or sets the job sample dye status.
        /// </summary>
        [NotMapped]
        [JsonIgnore]
        public SampleDyeStatuses JobSampleDyeStatus
        {
            get { return (SampleDyeStatuses)SampleDyeStatus; }
            set { SampleDyeStatus = value.ToInt32(); RaisePropertyChangedAuto(); }
        }

        private JobDesignations _designation;
        [NotMapped]
        [JsonIgnore]
        public JobDesignations Designation
        {
            get { return _designation; }
            set { _designation = value; RaisePropertyChangedAuto(); }
        }

        [NotMapped]
        [JsonIgnore]
        public EditingStates JobEditingState
        {
            get { return (EditingStates)EditingState; }
            set { EditingState = value.ToInt32(); RaisePropertyChangedAuto(); }
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handles the CollectionChanged event of the Segments collection.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
        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));
        }

        /// <summary>
        /// Handles the PropertyChanged event of all job segments.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
        private void Segment_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(Segment.Length))
            {
                OnLengthChanged();
            }
        }

        #endregion

        #region Virtual Methods

        /// <summary>
        /// Called when the <see cref="Length"/> property has been changed
        /// </summary>
        protected virtual void OnLengthChanged()
        {
            if (_lastLength != GetLength())
            {
                RaisePropertyChanged(nameof(Length));
                RaisePropertyChanged(nameof(LengthIncludingNumberOfUnits));
                LengthChanged?.Invoke(this, new EventArgs());
            }
        }

        #endregion

        #region Override Methods

        /// <summary>
        /// Raises the property changed event.
        /// </summary>
        /// <param name="propName">Name of the property.</param>
        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;
        }

        public override void DefferedDelete(ObservablesContext context)
        {
            Segments.ToList().ForEach(x => x.DefferedDelete(context));
            Segments.Clear();
            base.DefferedDelete(context);
        }

        #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;
        }

        /// <summary>
        /// Adds a new solid segment.
        /// </summary>
        public Segment AddSolidSegment()
        {
            return AddSolidSegment(System.Windows.Media.Colors.Black);
        }

        /// <summary>
        /// Adds a new solid segment.
        /// </summary>
        public Segment AddSolidSegment(System.Windows.Media.Color color)
        {
            return AddSolidSegment(color, 10);
        }

        /// <summary>
        /// Adds a new solid segment.
        /// </summary>
        public Segment AddSolidSegment(double length)
        {
            return AddSolidSegment(System.Windows.Media.Colors.White, length);
        }

        /// <summary>
        /// Adds a new solid segment.
        /// </summary>
        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;
        }

        /// <summary>
        /// Adds a new gradient segment.
        /// </summary>
        public Segment AddGradientSegment()
        {
            return AddGradientSegment(10);
        }

        /// <summary>
        /// Adds a new gradient segment.
        /// </summary>
        public Segment AddGradientSegment(double length)
        {
            var segment = AddSolidSegment(length);
            segment.BrushStops.Last().Color = System.Windows.Media.Colors.Silver;
            segment.AddBrushStop();
            segment.BrushStops.Last().Color = System.Windows.Media.Colors.DimGray;
            return segment;
        }

        /// <summary>
        /// Gets the duration estimation for this job.
        /// </summary>
        /// <param name="processParameters">The process parameters.</param>
        /// <returns></returns>
        public TimeSpan GetEstimatedDuration(ProcessParametersTable processParameters)
        {
            if (processParameters.DyeingSpeed == 0)
            {
                throw new ArgumentException("Process parameters dying speed cannot be zero.");
            }
            return TimeSpan.FromSeconds((LengthIncludingNumberOfUnits + processParameters.DryerBufferLength) / (processParameters.DyeingSpeed / 100d));
        }

        /// <summary>
        /// Gets the duration estimation for this job.
        /// </summary>
        /// <returns></returns>
        public Task<TimeSpan> GetEstimatedDuration()
        {
            return Task.Factory.StartNew<TimeSpan>(() => 
            {
                var process = GetRecommendedProcessParameters().Result;
                return GetEstimatedDuration(process);
            });
        }

        /// <summary>
        /// Translates the job progress to time.
        /// </summary>
        /// <param name="progress">The progress.</param>
        /// <param name="processParameters">The process parameters.</param>
        /// <returns></returns>
        public static TimeSpan TranslateProgressToTime(double progress, ProcessParametersTable processParameters)
        {
            return TimeSpan.FromSeconds(progress / (processParameters.DyeingSpeed / 100d));
        }

        /// <summary>
        /// Creates an inter segment.
        /// </summary>
        /// <param name="length">The length.</param>
        /// <returns></returns>
        public static Segment CreateInterSegment(double length)
        {
            return new Segment()
            {
                IsInterSegment = true,
                Length = length,
                Name = "Inter Segment",
                BrushStops = new SynchronizedObservableCollection<BrushStop>()
                            {
                                new BrushStop()
                                {
                                     ColorSpace = new ColorSpace(),
                                     Color = System.Windows.Media.Colors.White,
                                }
                            },
            };
        }

        public Task<ProcessParametersTable> GetRecommendedProcessParameters()
        {
            return Task.Factory.StartNew<ProcessParametersTable>(() =>
            {
                try
                {
                    int index = ColorConversion.TangoColorConverter.GetLeastCommonProcessParametersTableIndex(Segments.SelectMany(x => x.BrushStops));
                    return Rml.GetActiveProcessGroup().ProcessParametersTables[index];
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("Could not calculate the recommended process parameters for the job.", ex);
                }
            });
        }

        public Task<JobFile> ToJobFile()
        {
            return Task.Factory.StartNew<JobFile>(() =>
            {
                using (ObservablesContext db = ObservablesContext.CreateDefault())
                {
                    var job = new JobBuilder(db).Set(Guid).WithUser().WithRML().WithSegments().WithBrushStops().Build();

                    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;

                    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);
                            s.BrushStops.Add(st);
                        }
                    }

                    return jobFile;
                }
            });
        }

        public static Task<Job> FromJobFile(JobFile jobFile, String machineGuid, String userGuid)
        {
            return Task.Factory.StartNew(() =>
            {
                using (ObservablesContext db = ObservablesContext.CreateDefault())
                {
                    var job = new Job();
                    job.MachineGuid = machineGuid;
                    job.UserGuid = userGuid;

                    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;


                    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.");

                            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 catalog color could not be located on database.");
                            }

                            BrushStop st = new BrushStop();
                            st.StopIndex = j + 1;
                            st.SegmentGuid = s.Guid;
                            stop.MapPrimitivesWithStringsNoNullsTo(st);
                            s.BrushStops.Add(st);
                        }
                    }

                    return job;
                }
            });
        }

        #endregion

        #region Validation

        protected override void OnValidating(ObservablesContext context)
        {
            base.OnValidating(context);

            if (String.IsNullOrWhiteSpace(Name))
            {
                InsertError(nameof(Name), "Job name is required");
            }
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="Job" /> class.
        /// </summary>
        public Job() : base()
        {

        }
    }
}