aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/SideChains/RealTimeGraphEx/ReachGraphs/RealTimeGraphExReachCircle.cs
blob: 0e1a95b5bac248c1ab467942c57c67fcea71f6bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
using RealTimeGraphEx.Controllers;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace RealTimeGraphEx.ReachGraphs
{
    public class RealTimeGraphExReachCircle : RealTimeGraphExBase
    {
        #region Protected Fields

        protected int updateCounter;
        protected Ellipse ellipse;
        protected double lastValue;

        #endregion

        #region Cross Thread Fields

        protected Brush _stroke;
        protected Brush _fill;
        protected GraphController _graphController;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the graph strokes color.
        /// </summary>
        public Brush Stroke
        {
            get { return (Brush)GetValue(StrokeProperty); }
            set { SetValue(StrokeProperty, value); }
        }
        public static readonly DependencyProperty StrokeProperty =
            DependencyProperty.Register("Stroke", typeof(Brush), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(Brushes.Black, new PropertyChangedCallback(CrossModelChanged)));

        /// <summary>
        /// Gets or sets the graph fill color.
        /// </summary>
        public Brush Fill
        {
            get { return (Brush)GetValue(FillProperty); }
            set { SetValue(FillProperty, value); }
        }
        public static readonly DependencyProperty FillProperty =
            DependencyProperty.Register("Fill", typeof(Brush), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(Brushes.Gray, new PropertyChangedCallback(CrossModelChanged)));

        /// <summary>
        /// Gets or sets the IDataSeries used to push data points to the graph.
        /// </summary>
        public GraphController Controller
        {
            get { return (GraphController)GetValue(ControllerProperty); }
            set { SetValue(ControllerProperty, value); }
        }
        public static readonly DependencyProperty ControllerProperty =
            DependencyProperty.Register("Controller", typeof(GraphController), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(null, new PropertyChangedCallback(GraphControllerChanged)));
        private static void GraphControllerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = d as RealTimeGraphExReachCircle;

            if (control.Controller != null)
            {
                control.Controller.RegisterMethods(control.ClearGraph, control.StartPushThread, control.SetPaused, control.ChangeRenderMode, null);
                control._graphController = control.Controller;
            }
        }

        /// <summary>
        /// Gets or sets the collection of VU range data determines how to fill the VU graph.
        /// </summary>
        public ObservableCollection<Models.RangeData> Ranges
        {
            get { return (ObservableCollection<Models.RangeData>)GetValue(RangesProperty); }
            set { SetValue(RangesProperty, value); }
        }
        public static readonly DependencyProperty RangesProperty =
            DependencyProperty.Register("Ranges", typeof(ObservableCollection<Models.RangeData>), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets whether to animate the circle when when it's value doens not change.
        /// </summary>
        public bool Animate
        {
            get { return (bool)GetValue(AnimateProperty); }
            set { SetValue(AnimateProperty, value); }
        }
        public static readonly DependencyProperty AnimateProperty =
            DependencyProperty.Register("Animate", typeof(bool), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(false));

        /// <summary>
        /// Gets or sets the height value to animate when the Animate property is set to true.
        /// </summary>
        public double AnimationAmount
        {
            get { return (double)GetValue(AnimationAmountProperty); }
            set { SetValue(AnimationAmountProperty, value); }
        }
        public static readonly DependencyProperty AnimationAmountProperty =
            DependencyProperty.Register("AnimationAmount", typeof(double), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(10.0));

        /// <summary>
        /// Gets or sets the duration of the animation when the Animate property is set to true.
        /// </summary>
        public Duration AnimationDuration
        {
            get { return (Duration)GetValue(AnimationDurationProperty); }
            set { SetValue(AnimationDurationProperty, value); }
        }
        public static readonly DependencyProperty AnimationDurationProperty =
            DependencyProperty.Register("AnimationDuration", typeof(Duration), typeof(RealTimeGraphExReachCircle), new PropertyMetadata(new Duration(TimeSpan.FromMilliseconds(500))));



        #endregion

        #region Constructors

        public RealTimeGraphExReachCircle()
            : base()
        {
            Ranges = new ObservableCollection<Models.RangeData>();
        }

        #endregion

        #region Override Methods

        protected override void Initialize()
        {
            base.Initialize();

            if (ellipse == null)
            {
                ellipse = new Ellipse() { Tag = "Default" };
                ellipse.Height = 0;
            }

            gridLinesAndImageWrapperGrid.Children.Remove(img);

            if (!gridLinesAndImageWrapperGrid.Children.Contains(ellipse))
            {
                gridLinesAndImageWrapperGrid.Children.Add(ellipse);
            }

            //TODO: Base on developer selection vertical/horizontal VU.
            ellipse.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
            ellipse.VerticalAlignment = System.Windows.VerticalAlignment.Center;
        }

        protected override void OnSetCrossThreadFields()
        {
            base.OnSetCrossThreadFields();

            this.Dispatcher.Invoke(() =>
            {
                _graphController = Controller;
                _stroke = Stroke;
                _fill = Fill;

                if (ellipse.Tag != null && ellipse.Tag.ToString() == "Default")
                {
                    ellipse.Fill = Fill;
                    ellipse.Stroke = Stroke;
                }

                if (_graphController != null && _graphController.dataSeries != null && _graphController.dataSeries.useFillandStroke)
                {
                    ellipse.Fill = _graphController.dataSeries.fill;
                    ellipse.Stroke = _graphController.dataSeries.stroke;
                }

            }, DispatcherPriority.Send);
        }

        protected override void OnClearGraph()
        {
            base.OnClearGraph();

            this.Dispatcher.Invoke(() =>
            {
                ellipse.BeginAnimation(WidthProperty, null);
                ellipse.BeginAnimation(HeightProperty, null);
                ellipse.Height = 0;
                ellipse.Width = 0;
            });
        }

        protected internal override void OnRenderGraph()
        {
            if (_graphController != null && _graphController.dataSeries.Points != null && _graphController.dataSeries.Points.Count > 0 && _width > 1 && _height > 1)
            {
                double value = _graphController.dataSeries.Points[_graphController.dataSeries.Points.Count - 1]; //Get last value.

                if (value == lastValue)
                {
                    _graphController.ClearPoints();
                    return;
                }

                lastValue = value;

                double valueHeightPrecentage = ConvertYToImageY(value);
                double valueWidthPrecentage = ConvertYToImageX(value);

                updateCounter++;

                if (updateCounter >= 1 && !_isPaused)
                {
                    updateCounter = 0;
                    OnDrawVisuals(valueHeightPrecentage, valueWidthPrecentage);
                }

                _graphController.ClearPoints();
            }
        }

        #endregion

        #region Virtual Methods

        protected virtual void OnDrawVisuals(double valueHeightPrecentage, double valueWidthPrecentage)
        {
            this.Dispatcher.Invoke(() =>
            {

                ellipse.BeginAnimation(WidthProperty, null);
                ellipse.BeginAnimation(HeightProperty, null);

                ellipse.Height = valueHeightPrecentage;
                ellipse.Width = valueWidthPrecentage;

                ellipse.Fill = GetEllipseBrush();

                if (Animate)
                {
                    DoubleAnimation ani = new DoubleAnimation();
                    ani.CurrentTimeInvalidated += (x, y) =>
                    {
                        ellipse.Fill = GetEllipseBrush();
                    };
                    ani.To = ellipse.Width - AnimationAmount;
                    ani.From = ellipse.Width;
                    ani.RepeatBehavior = RepeatBehavior.Forever;
                    ani.AutoReverse = true;
                    ani.Duration = AnimationDuration;

                    ellipse.BeginAnimation(WidthProperty, ani);
                    ellipse.BeginAnimation(HeightProperty, ani);
                }
            });
        }

        private Brush GetEllipseBrush()
        {
            if (Ranges != null && Ranges.Count > 0)
            {
                RadialGradientBrush rangesBrush = new RadialGradientBrush();
                rangesBrush.MappingMode = BrushMappingMode.Absolute;
                rangesBrush.Center = new Point(ellipse.Width / 2, ellipse.Height / 2);
                rangesBrush.GradientOrigin = new Point(ellipse.Width / 2, ellipse.Height / 2);
                rangesBrush.RadiusX = _width;
                rangesBrush.RadiusY = _height;

                for (int i = Ranges.Count - 1; i >= 0; i--)
                {
                    var range = Ranges[i];

                    double rangeValuePrecentage = ConvertYToImageY(range.Value);

                    GradientStop stop = new GradientStop();
                    stop.Color = range.Color;
                    stop.Offset = ((rangeValuePrecentage * 100) / _height) / 100;
                    rangesBrush.GradientStops.Add(stop);
                }

                return rangesBrush;
            }

            return Fill;
        }

        #endregion
    }
}
class="o">= value; RaisePropertyChangedAuto(); } } private ICollectionView _segmentsCollectionView; /// <summary> /// Gets or sets the job segments collection view. /// </summary> public ICollectionView SegmentsCollectionView { get { return _segmentsCollectionView; } set { _segmentsCollectionView = value; RaisePropertyChangedAuto(); } } private List<ColorSpace> _colorSpaces; /// <summary> /// Gets or sets the available color spaces. /// </summary> public List<ColorSpace> ColorSpaces { get { return _colorSpaces; } set { _colorSpaces = value; RaisePropertyChangedAuto(); } } private List<Rml> _rmls; /// <summary> /// Gets or sets the available RMLS. /// </summary> public List<Rml> Rmls { get { return _rmls; } set { _rmls = value; RaisePropertyChangedAuto(); } } private Rml _selectedRML; /// <summary> /// Gets or sets the selected RML. /// </summary> public Rml SelectedRML { get { return _selectedRML; } set { _selectedRML = value; RaisePropertyChangedAuto(); OnSelectedRmlChanged(); } } private List<SpoolType> _spoolTypes; /// <summary> /// Gets or sets the available spool types. /// </summary> public List<SpoolType> SpoolTypes { get { return _spoolTypes; } set { _spoolTypes = value; RaisePropertyChangedAuto(); } } private List<Customer> _customers; /// <summary> /// Gets or sets the available customers. /// </summary> public List<Customer> Customers { get { return _customers; } set { _customers = value; RaisePropertyChangedAuto(); } } private String _customersFilter; /// <summary> /// Gets or sets the customers filter. /// </summary> public String CustomersFilter { get { return _customersFilter; } set { _customersFilter = value; RaisePropertyChangedAuto(); } } /// <summary> /// Gets or sets the customers automatic complete provider. /// </summary> public AutoCompleteProvider<Customer> CustomersAutoCompleteProvider { get; set; } private ObservableCollection<FineTuneItem> _fineTuneItems; /// <summary> /// Gets or sets the fine tune items. /// </summary> public ObservableCollection<FineTuneItem> FineTuneItems { get { return _fineTuneItems; } set { _fineTuneItems = value; RaisePropertyChangedAuto(); } } private ObservableCollection<FineTuneItem> _approvalFineTuneItems; /// <summary> /// Gets or sets the fine tune items. /// </summary> public ObservableCollection<FineTuneItem> ApprovalFineTuneItems { get { return _approvalFineTuneItems; } set { _approvalFineTuneItems = value; RaisePropertyChangedAuto(); } } private List<ColorCatalog> _availableCatalogs; public List<ColorCatalog> AvailableCatalogs { get { return _availableCatalogs; } set { _availableCatalogs = value; RaisePropertyChangedAuto(); } } private bool _isFullMode; public bool IsFullMode { get { return _isFullMode; } set { _isFullMode = value; RaisePropertyChangedAuto(); } } private bool _isSummaryOpened; public bool IsSummaryOpened { get { return _isSummaryOpened; } set { _isSummaryOpened = value; RaisePropertyChangedAuto(); } } #endregion #region Commands /// <summary> /// Gets or sets the add solid segment command. /// </summary> public RelayCommand<ISegmentModel> AddNewSegmentCommand { get; set; } /// <summary> /// Gets or sets the remove segment command. /// </summary> public RelayCommand<SegmentModel> RemoveSegmentCommand { get; set; } /// <summary> /// Gets or sets the copy segment command. /// </summary> public RelayCommand<SegmentModel> DuplicateSegmentCommand { get; set; } /// <summary> /// Gets or sets the remove job command. /// </summary> public RelayCommand<SegmentModel> AddColorCommand { get; set; } /// <summary> /// Gets or sets the remove job command. /// </summary> public RelayCommand<BrushStopModel> EditColorCommand { get; set; } /// <summary> /// Gets or sets the replace brush stop command. /// </summary> public RelayCommand<BrushStop> ReplaceBrushStopCommand { get; set; } /// <summary> /// Gets or sets the dye command. /// </summary> public RelayCommand DyeCommand { get; set; } /// <summary> /// Gets or sets the export embroidery command. /// </summary> public RelayCommand ExportEmbroideryCommand { get; set; } public RelayCommand EditJobDetailsCommand { get; set; } public RelayCommand RepeatUnitsCommand { get; set; } public RelayCommand JobModeSwitchCommand { get; set; } public RelayCommand<SegmentsGroupModel> UngroupSegmentsCommand { get; set; } public RelayCommand<SegmentsGroupModel> DeleteSegmentsGroupCommand { get; set; } public RelayCommand<SegmentsGroupModel> RepeatSegmentsGroupCommand { get; set; } #endregion #region collapsed mode commands public RelayCommand InsertWhiteGapCommand { get; set; } public RelayCommand ReverseCommand { get; set; } public RelayCommand DeleteSegmentCommand { get; set; } public RelayCommand RepeateSegmentCommand { get; set; } public RelayCommand PasteCommand { get; set; } public RelayCommand CopyCommand { get; set; } public RelayCommand UndoCommand { get; set; } public RelayCommand RedoCommand { get; set; } #endregion #region Constructors /// <summary> /// Initializes the <see cref="JobViewVM"/> class. /// </summary> static JobViewVM() { _jobs_fine_tune_items = new Dictionary<string, List<FineTuneItem>>(); } /// <summary> /// Initializes a new instance of the <see cref="JobViewVM"/> class. /// </summary> public JobViewVM() { _converter = new DefaultColorConverter(); _volumeConversionTimer = new ActionTimer(TimeSpan.FromMilliseconds(50)); RegisterForMessage<JobSelectedMessage>(HandleJobSelectedMessage); FineTuneItems = new ObservableCollection<FineTuneItem>(); ApprovalFineTuneItems = new ObservableCollection<FineTuneItem>(); CustomersAutoCompleteProvider = new AutoCompleteProvider<Customer>((customer, filter) => { return customer.Name.ToLower().StartsWith(filter != null ? filter.ToLower() : String.Empty); }); //Initialize Commands AddNewSegmentCommand = new RelayCommand<ISegmentModel>(AddNewSegment); RemoveSegmentCommand = new RelayCommand<SegmentModel>(RemoveSegment); DuplicateSegmentCommand = new RelayCommand<SegmentModel>(DuplicateSegment); AddColorCommand = new RelayCommand<SegmentModel>(AddColor); EditColorCommand = new RelayCommand<BrushStopModel>(EditColor); DyeCommand = new RelayCommand(StartJob, CanStartJob); ExportEmbroideryCommand = new RelayCommand(ExportEmbroidery); RepeatUnitsCommand = new RelayCommand(RepeatUnits); EditJobDetailsCommand = new RelayCommand(EditJobDetails); JobModeSwitchCommand = new RelayCommand(JobModeSwitch); InsertWhiteGapCommand = new RelayCommand(InsertWhiteGap); ReverseCommand = new RelayCommand(Reverse); DeleteSegmentCommand = new RelayCommand(DeleteSegments); DeleteSegmentsGroupCommand = new RelayCommand<SegmentsGroupModel>(DeleteSegmentsGroup); RepeateSegmentCommand = new RelayCommand(RepeateSegments); UngroupSegmentsCommand = new RelayCommand<SegmentsGroupModel>(UngroupSegments); RepeatSegmentsGroupCommand = new RelayCommand<SegmentsGroupModel>(RepeatSegmentsGroup); PasteCommand = new RelayCommand(Paste); CopyCommand = new RelayCommand(Copy); UndoCommand = new RelayCommand(Undo);//(x) => { return UndoRedoManager.Instance.IsEnableUndoOperation(); } RedoCommand = new RelayCommand(Redo);//(x) => { return UndoRedoManager.Instance.IsEnableRedoOperation();} IsFullMode = true; IsSummaryOpened = true; _not_show_warning = false; } #endregion #region Job Management /// <summary> /// Loads the job. /// </summary> private async void LoadJob() { try { if (!(_job_to_load == null || (_job_to_load != null && Job != null && _job_to_load.Guid == Job.Guid))) { //View.ScrollToTop(); LogManager.Log($"Loading selected job '{_job_to_load.Name}'..."); IsFree = false; _can_navigate_back = false; if (_db != null) { if (Job != null) { //Job.RmlChanged -= OnRmlChanged; Job.NameChanged -= Job_NameChanged; } if (Rmls != null) { Rmls.Where(x => x.Cct != null && x.Cct.Data != null).ToList().ForEach(x => x.Cct.Data = null); Rmls.ForEach(x => x.Cct = null); if (SelectedRML != null) { SelectedRML.Cct = null; SelectedRML = null; } Rmls = null; } _db.Dispose(); GC.Collect(); } _db = ObservablesContext.CreateDefault(); _catalogs = await new CatalogsCollectionBuilder(_db) .SetAll() .WithGroups() .WithItems() .ForSite(MachineProvider.Machine.SiteGuid) .BuildListAsync(); Job = await new JobBuilder(_db).Set(_job_to_load.Guid) .WithConfiguration() .WithUser() .WithSegments() .WithBrushStops() .WithSegmentsGroups() .BuildAsync(); Job.NameChanged -= Job_NameChanged; Job.NameChanged += Job_NameChanged; Job.ValidateOnPropertyChanged = true; //GetLubricationLevel(); //await SetSpoolTension(Job.Rml); LogManager.Log("Loading RMLS..."); Rmls = (await new RmlsCollectionBuilder(_db).SetAll().ForHeadType(MachineProvider.Machine.MachineHeadType).ForSite(MachineProvider.Machine.SiteGuid).BuildAsync()).OrderBy(x => x.FinalName).ToList(); // for test only: Rmls = (await new RmlsCollectionBuilder(_db).SetAll().BuildAsync()).OrderBy(x => x.FinalName).ToList(); LogManager.Log("Loading Color Spaces..."); ColorSpaces = await _db.ColorSpaces.Where(x => x.Code != (int)BL.Enumerations.ColorSpaces.CMYK).ToListAsync(); LogManager.Log("Loading Spool Types..."); SpoolTypes = await _db.SpoolTypes.ToListAsync(); LogManager.Log("Loading Customers..."); Customers = await _db.Customers.Where(x => x.OrganizationGuid == MachineProvider.Machine.OrganizationGuid).ToListAsync(); AvailableCatalogs = await new CatalogsCollectionBuilder(_db).SetAll().WithGroups().WithItems().ForSite(MachineProvider.Machine.SiteGuid).BuildListAsync(); _selectedRML = Job.Rml; RaisePropertyChanged(nameof(SelectedRML)); await LoadRML(_selectedRML); LoadJobModel(); _job_to_load = null; _current_job_string = Job.ToJobFileWhenLoaded().ToString(); } if (!_jobs_fine_tune_items.ContainsKey(Job.Guid) && Job.JobFineTuningStatus == BL.Enumerations.FineTuningStatuses.PendingApproval) { Job.JobFineTuningStatus = BL.Enumerations.FineTuningStatuses.Unspecified; } LogManager.Log($"Job editing state = '{Job.JobEditingState}'."); //if (Job.JobEditingState == BL.Enumerations.EditingStates.SampleDye && Job.JobSampleDyeStatus == BL.Enumerations.SampleDyeStatuses.PendingApproval) //{ // LogManager.Log("Directing view to display sample dye region."); // View.DisplaySampleDye(); //} //else if (Job.JobEditingState == BL.Enumerations.EditingStates.FineTuning && Job.JobFineTuningStatus == BL.Enumerations.FineTuningStatuses.PendingApproval) //{ // LogManager.Log("Directing view to display fine tuning region."); // View.DisplayFineTuning(); //} IsFullMode = true; DyeCommand.RaiseCanExecuteChanged(); } catch (Exception ex) { IsFree = true; LogManager.Log(ex, $"Error loading job '{(_job_to_load != null ? _job_to_load.Name : "null")}'"); await NotificationProvider.ShowError("An error occurred while trying to load the selected job."); _can_navigate_back = true; await NavigationManager.NavigateBack(); } finally { IsFree = true; } } private void LoadJobModel() { var jobModel = new JobModel(ColorSpaces) { Name = Job.Name, CreationDate = Job.CreationDate, LengthPercentageFactor = Job.LengthPercentageFactor, NumberOfUnits = Job.NumberOfUnits, IsAllSegmentsPerSpool = Job.IsAllSegmentsPerSpool, Rml = Job.Rml, ColorSpace = Job.ColorSpace, SpoolType = Job.SpoolType, User = Job.User, Machine = Job.Machine, JobType = Job.JobType, InterSegmentLength = Job.InterSegmentLength, EnableInterSegment = Job.EnableInterSegment }; Dictionary<string, SegmentsGroupModel> guidToGroup = new Dictionary<string, SegmentsGroupModel>(); if (Job.Version < 2) { int segmentindex = 1; foreach (var segm in Job.OrderedSegments) { if(segm.BrushStops.Count > 1) { var brushes = segm.BrushStops; Segment currentSegment = segm; double lengthOfOldSegment = segm.Length; for (int index = 0; index < (brushes.Count - 1); index++) { SegmentModel csegmentModel = new SegmentModel(jobModel, segm.Guid) { Name = segm.Name, Length = segm.Length, SegmentIndex = segm.SegmentIndex, IsInterSegment = segm.IsInterSegment, Job = jobModel }; csegmentModel.SegmentIndex = segmentindex++; brushes[index].Segment = currentSegment; brushes[index + 1].Segment = currentSegment; csegmentModel.Length = (lengthOfOldSegment * (brushes[index + 1].OffsetPercent - brushes[index].OffsetPercent) / 100d); BrushStopModel brushStopModelFirst = new BrushStopModel(brushes[index], csegmentModel, 1); BrushStopModel brushStopModelSecond = new BrushStopModel(brushes[index + 1], csegmentModel, 1); csegmentModel.CreateGradientBrushes(brushStopModelFirst, brushStopModelSecond); jobModel.Segments.Add(csegmentModel); } } else { SegmentModel segmentModel = LoadSegmentModel(segm, jobModel); segmentModel.SegmentIndex = segmentindex++; jobModel.Segments.Add(segmentModel); } } } else { var segments = Job.OrderedSegmentsWithGroups; foreach( var segment in segments) { if (segment is Segment simpleSegment) { SegmentModel segmentModel = LoadSegmentModel(simpleSegment, jobModel); jobModel.Segments.Add(segmentModel); } else if (segment is SegmentsGroup group) { SegmentsGroupModel segmentsGroupModel = new SegmentsGroupModel(jobModel) { SegmentIndex = group.SegmentIndex, Repeats = group.Repeats }; foreach (var innerSegment in group.OrderedSegments) { SegmentModel segmentModel = LoadSegmentModel(innerSegment, jobModel); jobModel.Segments.Add(segmentModel); segmentsGroupModel.Segments.Add(segmentModel); segmentModel.SegmentsGroupModel = segmentsGroupModel; } jobModel.SegmentsGroups.Add(segmentsGroupModel); } } } jobModel.InterSegmentLength = Job.EnableInterSegment ? Job.InterSegmentLength : 0; jobModel.LoadGroupingSegments(); JobModel = jobModel; //create grouping SegmentsCollectionView = CollectionViewSource.GetDefaultView(JobModel.GroupingSegments); SegmentsCollectionView.SortDescriptions.Add(new SortDescription(nameof(SegmentModel.SegmentIndex), ListSortDirection.Ascending)); UndoRedoManager.Instance.ClearAll(); ArrangeSegmentsIndixes(); JsonSerializerSettings settings = new JsonSerializerSettings() { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore, PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects }; _jsonJobModelLoaded = JsonConvert.SerializeObject(JobModel , Formatting.Indented, settings); } /// <summary> /// Loads the segment model. /// </summary> /// <param name="segm">The segm.</param> /// <param name="jobModel">The job model.</param> /// <returns></returns> private SegmentModel LoadSegmentModel(Segment segm, JobModel jobModel) { SegmentModel segmentModel = new SegmentModel(jobModel, segm.Guid) { Name = segm.Name, Length = segm.Length, SegmentIndex = segm.SegmentIndex, IsInterSegment = segm.IsInterSegment, Job = jobModel, }; foreach (var brushStop in segm.BrushStops) { BrushStopModel brushStopModel = new BrushStopModel(brushStop, segmentModel, Job.Version); segmentModel.BrushStops.Add(brushStopModel); } segmentModel.ArrangeBrushStopsPosition(); segmentModel.UpdateMiddleColorBrush(); return segmentModel; } private void Job_NameChanged(object sender, string e) { DyeCommand.RaiseCanExecuteChanged(); } /// <summary> /// Starts the job. /// </summary> private async void StartJob() { if (startingJob) return; try { Debug.WriteLine("Job Starting..."); startingJob = true; LogManager.Log("Start job command pressed. Starting job and navigating to job progress view..."); await Save(); var handler = await PrintingManager.Print(Job, _db); await NavigationManager.NavigateTo<JobsV2Module>(nameof(JobProgressView)); startingJob = false; } catch (InsufficientLiquidQuantityException) { //Ignore.. } catch (OperationCanceledException) { //Ignore.. } catch (Exception ex) { LogManager.Log(ex, "Could not start the current job."); await NotificationProvider.ShowError($"{ex.Message}."); } finally { startingJob = false; } } /// <summary> /// Determines whether this instance [can start job]. /// </summary> private bool CanStartJob() { try { //var test = JobModel != null && !JobModel.Segments.SelectMany(x => x.BrushStops).Where(x => x.Position == BrushStopModel.PositionStatus.FirstColor || x.Position == BrushStopModel.PositionStatus.SecondColor).ToList().Exists(x => x.IsOutOfGamut); //return Job != null && Job.Validate(_db) && !Job.Segments.SelectMany(x => x.BrushStops).Where(x => !x.IsTransparent && !x.IsWhite).ToList().Exists(x => x.IsOutOfGamut || (x.BrushColorSpace == BL.Enumerations.ColorSpaces.Volume && x.IsLiquidVolumesOutOfRange)); return JobModel != null && !JobModel.Segments.ToList().Exists(x => x.BrushStops.Count == 0); } catch (Exception ex) { Debug.WriteLine(ex); return false; } } private async void EditJobDetails() { try { LogManager.Log("Editing the job details."); JobCreationViewVM vm = new JobCreationViewVM(_spoolTypes.ToList(), _rmls.ToList(), JobModel.InterSegmentLength, true); vm.JobName = JobModel.Name; vm.SelectedRML = JobModel.Rml; vm.SelectedSpoolType = JobModel.SpoolType; vm = await NotificationProvider.ShowDialog<JobCreationViewVM>(vm); if (!vm.DialogResult) return; Job.Name = vm.JobName; JobModel.Name = vm.JobName; Job.InterSegmentLength = vm.WhiteGap; Job.EnableInterSegment = vm.WhiteGap > 0; JobModel.InterSegmentLength = vm.WhiteGap; JobModel.EnableInterSegment = vm.WhiteGap > 0; Job.SpoolType = vm.SelectedSpoolType; JobModel.SpoolType = vm.SelectedSpoolType;//update length!!!! SelectedRML = vm.SelectedRML; if (vm.IsDuplicate) { try { await Save(); //Duplicate new job var cloned = Job.Clone(); cloned.JobIndex = (_db.Jobs.Max(x => x.JobIndex) + 1); _db.Jobs.Add(cloned); await _db.SaveChangesAsync(); _job_to_load = cloned; LoadJob(); } catch (Exception ex) { LogManager.Log(ex, "Error duplicate job."); } } } catch (Exception ex) { LogManager.Log(ex, "Error editing the job."); } } private async void RepeatUnits() { var maxLength = Job.SpoolType.Length; var maxRep = (maxLength == 0 ? 999 : (maxLength / JobModel.Length)); var vm = await NotificationProvider.ShowDialog<RepeatJobViewVM>(new RepeatJobViewVM("Repeat All", JobModel.NumberOfUnits) { MaxRepeations = (int)maxRep }); if (vm.DialogResult) { JobModel.NumberOfUnits = vm.Repeats; } } #endregion #region RML Changed private async void OnSelectedRmlChanged() { await LoadRML(SelectedRML); } private async Task LoadRML(Rml rml) { if (rml != null) { if (Job.Rml != rml || rml.Cct == null) { bool updateRML = Job.Rml != rml; Job.Rml = await new RmlBuilder(_db) .Set(rml.Guid) .WithActiveParametersGroup() .WithCCT() .WithCAT(MachineProvider.Machine.Guid) .WithLiquidFactors() .WithSpools() .BuildAsync(); if (JobModel != null) JobModel.Rml = Job.Rml; if (updateRML && JobModel != null) { NotificationProvider.SetGlobalBusyMessage("Updating IsOutOfGammut due to the change RML..."); foreach (var segment in JobModel.OrderedSegmentsWithGroups) { if (segment is SegmentModel innerSegment) { innerSegment.UpdateBrushStops(); } else if ( segment is SegmentsGroupModel group) { foreach( var segm in group.Segments) { segm.UpdateBrushStops(); } } } DyeCommand.RaiseCanExecuteChanged(); NotificationProvider.ReleaseGlobalBusyMessage(); } //if (updateVolumes) //{ // NotificationProvider.SetGlobalBusyMessage("Updating job liquid volumes..."); // foreach (var stop in Job.Segments.SelectMany(x => x.BrushStops).Where(x => x.BrushColorSpace == BL.Enumerations.ColorSpaces.RGB || x.BrushColorSpace == BL.Enumerations.ColorSpaces.LAB).ToList()) // { // try // { // var output = await _converter.ConvertAsync(stop, false, false); // output.ApplyOnBrushStopVolumesOnly(stop); // } // catch (Exception ex) // { // LogManager.Log(ex, $"Error updating stop volumes after changing thread on segment {stop.Segment.SegmentIndex}, stop {stop.StopIndex}."); // } // } // NotificationProvider.ReleaseGlobalBusyMessage(); //} } } } #endregion #region Segments Management /// <summary> /// Adds a new segment. /// </summary> private void AddNewSegment(ISegmentModel segment) { try { LogManager.Log("Adding new segment..."); UndoRedoManager.Instance.InsertAndExecuteCommand(new AddNewSegmentCommand(JobModel, segment, Settings.DefaultSegmentLength > 0 ? Settings.DefaultSegmentLength : 10)); ArrangeSegmentsIndixes(); DyeCommand.RaiseCanExecuteChanged(); } catch (Exception ex) { LogManager.Log(ex, "Could not add a new segment."); NotificationProvider.ShowError("An error occurred while trying to add a new segment."); } } private async void RepeatSegmentsGroup( SegmentsGroupModel group) { var maxLength = Job.SpoolType.Length == 0 ? 999 : Job.SpoolType.Length; var maxRep = (maxLength - JobModel.Length)/ group.Length; var vm = await NotificationProvider.ShowDialog<RepeatJobViewVM>(new RepeatJobViewVM($"Edit \"Group {group.SegmentIndex}\" Repeat", group.Repeats) { MaxRepeations = (int)maxRep }); if (vm.DialogResult) { group.Repeats = vm.Repeats; } } /// <summary> /// Removes the segment. /// </summary> private async void RemoveSegment(SegmentModel segment) { if (JobModel.Segments.Count > 1) { try { if (await NotificationProvider.ShowQuestion("Are you sure you want to remove the selected segment?")) { UndoRedoManager.Instance.InsertAndExecuteCommand(new RemoveSegmentCommand(JobModel, segment)); ArrangeSegmentsIndixes(); DyeCommand.RaiseCanExecuteChanged(); } } catch (Exception ex) { LogManager.Log(ex, "Could not remove segment."); await NotificationProvider.ShowError("An error occurred while trying to delete segment."); } } else { await NotificationProvider.ShowInfo("A job must contain at least one color segment."); } } private async void DeleteSegmentsGroup(SegmentsGroupModel segmentsGroup) { if (JobModel.GroupingSegments.Count > 1) { try { if (await NotificationProvider.ShowQuestion("Are you sure you want to remove the selected group of segments?")) { UndoRedoManager.Instance.InsertAndExecuteCommand(new DeleteSegmentsGroupCommand(JobModel, segmentsGroup)); ArrangeSegmentsIndixes(); DyeCommand.RaiseCanExecuteChanged(); } } catch (Exception ex) { LogManager.Log(ex, "Could not remove group of segments."); await NotificationProvider.ShowError("An error occurred while trying to delete group of segments."); } } else { await NotificationProvider.ShowInfo("A job must contain at least one color segment."); } } /// <summary> /// Duplicates the segment. /// </summary> private void DuplicateSegment(SegmentModel segment) { UndoRedoManager.Instance.InsertAndExecuteCommand(new DuplicateSegmentCommand(JobModel, segment)); ArrangeSegmentsIndixes(); DyeCommand.RaiseCanExecuteChanged(); } private void ArrangeSegmentsIndixes() { JobModel.ArrangeSegmentsIndixes(); SegmentsCollectionView.Refresh(); } #endregion #region Brush Stops Management /// <summary> /// Click on AddColor button. Add newBrush; /// </summary> private async void AddColor(SegmentModel segment) { LogManager.Log($"Adding new color to segment {segment.SegmentIndex}."); if ((segment.IsGradient && segment.Length < 10) || (segment.HasColors && segment.Length < 5)) { await NotificationProvider.ShowInfo("Color transitions are best visible with segment length of 5 meters and above."); } var vm = await NotificationProvider.ShowDialog<ColorSelectionViewVM>(new ColorSelectionViewVM() { DialogEditObject = new ColorSelectionViewVM.DialogObject() { SelectedSegment = segment, BrushStopForEdit = new BrushStopModel(segment), IsEditingMode = false, Catalogs = _catalogs } }); if (vm.DialogResult) { AddBrushStop(segment, vm.SelectedBrushStop); DyeCommand.RaiseCanExecuteChanged(); } // SetSegmentLiquidVolumes(segment); } /// <summary> /// Click on Edit Color button. Add newBrush; /// </summary> private async void EditColor(BrushStopModel brushStop) { if (brushStop == null) { await NotificationProvider.ShowError("The edit brush is null!"); return; } SegmentModel segment = brushStop.SegmentModel; LogManager.Log($"Edit brush stop."); var vm = await NotificationProvider.ShowDialog<ColorSelectionViewVM>(new ColorSelectionViewVM() { DialogEditObject = new ColorSelectionViewVM.DialogObject() { SelectedSegment = segment, BrushStopForEdit = brushStop, IsEditingMode = true, Catalogs = _catalogs } }); if (vm.DialogResult) { UndoRedoManager.Instance.InsertAndExecuteCommand(new EditBrushStopColorCommand(segment, brushStop, vm.SelectedBrushStop)); DyeCommand.RaiseCanExecuteChanged(); } } /// <summary> /// Adds the brush stop. /// </summary> public async void AddBrushStop(SegmentModel segment, BrushStopModel newBrushStop) { if (newBrushStop == null || segment == null) return; if(segment.IsGradient) { if (false == _not_show_warning) { var vm = await NotificationProvider.ShowDialog<AddSegmentWarningDialogVM>(new AddSegmentWarningDialogVM()); if (vm.NotShow) { _not_show_warning = true; } } } UndoRedoManager.Instance.InsertAndExecuteCommand(new AddBrushStopCommand(JobModel, segment, newBrushStop)); ArrangeSegmentsIndixes(); } #endregion #region Job Selection Message /// <summary> /// Handles the job selected message. /// </summary> /// <param name="message">The message.</param> private void HandleJobSelectedMessage(JobSelectedMessage message) { _job_to_load = message.Job; } #endregion #region Sample Dye /// <summary> /// Starts a sample dye. /// </summary> //private async void StartSampleDye() //{ // try // { // LogManager.Log("Sample dye command pressed..."); // await PrintingManager.PrintSample(Job, _db); // await NavigationManager.NavigateTo<JobsV2Module>(nameof(JobProgressView)); // } // catch (Exception ex) // { // LogManager.Log(ex, $"Error executing sample dye for job {Job.Name}."); // await NotificationProvider.ShowError(ex.Message); // } //} #endregion #region Export Embroidery private async void ExportEmbroidery() { try { if (!StorageProvider.IsConnected) { await NotificationProvider.ShowError("No storage device connected."); return; } var result = await NavigationManager. NavigateForResult<StorageModule, Storage.Views.MainView, ExplorerFileItem, Storage.Models.StorageNavigationRequest>( new Storage.Models.StorageNavigationRequest() { Intent = Storage.Models.StorageNavigationIntent.SaveFile, DefaultFileName = Job.Name + Path.GetExtension(Job.EmbroideryFileName), Filter = Path.GetExtension(Job.EmbroideryFileName), Title = "Export Embroidery File", }); if (result != null) { File.WriteAllBytes(Path.HasExtension(result.Path) ? result.Path : result.Path + Path.GetExtension(Job.EmbroideryFileName), Job.EmbroideryFileData); await NotificationProvider.ShowSuccess("Embroidery file exported successfully."); } } catch (Exception ex) { LogManager.Log(ex, "Error exporting embroidery file."); await NotificationProvider.ShowError("An error occurred while trying to save the selected embroidery file."); } } #endregion #region IPPC ViewModel Overrides /// <summary> /// Called when the application has been started. /// </summary> public override void OnApplicationStarted() { base.OnApplicationStarted(); MachineProvider.MachineOperator.PrintingEnded += MachineOperator_PrintingEnded; } private void MachineOperator_PrintingEnded(object sender, Integration.Operation.PrintingEventArgs e) { if (IsVisible) { _start_printing_btn.Push(); } } /// <summary> /// Called when the navigation system has navigated to this VM view. /// </summary> public override void OnNavigatedTo() { if (!MachineProvider.MachineOperator.IsPrinting) { _start_printing_btn.Push(); } base.OnNavigatedTo(); LoadJob(); } public override void OnBeforeNavigatedTo() { base.OnBeforeNavigatedTo(); } public override void OnBeforeNavigatedFrom() { base.OnBeforeNavigatedFrom(); } /// <summary> /// Called when the navigation system has navigated from this VM view. /// </summary> public override void OnNavigatedFrom() { _start_printing_btn.Pop(); base.OnNavigatedFrom(); _job_to_load_intent = JobNavigationIntent.Default; } public override void OnNavigatedTo(PPCViewModel fromVM) { base.OnNavigatedTo(fromVM); } /// <summary> /// Called before the navigation system navigates back from this object. /// Return false to abort the navigation. /// </summary> /// <returns></returns> public async override Task<bool> OnNavigateBackRequest() { if (!IsFree) return false; try { if (!_can_navigate_back) { await Save(); Job = null; JobModel = null; SegmentsCollectionView = null; } return true; } catch (Exception ex) { LogManager.Log(ex, "Error saving job to database. " + ex.InnerException.Message); await NotificationProvider.ShowError("Error saving the current job."); return true; } } public override void OnApplicationReady() { base.OnApplicationReady(); _start_printing_btn = new StartPrintingButton(DyeCommand, MachineProvider.MachineOperator); } #endregion #region INavigationObjectReceiver public void OnNavigatedToWithObject(JobNavigationObject e) { _job_to_load_intent = e.Intent; _job_to_load = e.Job; } #endregion #region collapsed mode private void JobModeSwitch() { IsFullMode = !IsFullMode; //UndoRedoManager.Instance.ClearAll(); According to QA! request - #6314 } private void InsertWhiteGap() { JobModel.InsertWhiteGapToSelectedSegments(); } private void Reverse() { if (false == JobModel.GroupingSegments.ToList().Any(x => x.IsSelected)) return; UndoRedoManager.Instance.InsertAndExecuteCommand(new ReverseCommand(JobModel)); ArrangeSegmentsIndixes(); } private async void DeleteSegments() { if (!JobModel.HasSelectedItems) return; if(JobModel.GroupingSegments.ToList().Where(x => x.IsSelected).ToList().Count == JobModel.GroupingSegments.Count) { await NotificationProvider.ShowInfo("A job must contain at least one segment. Please, change selection."); return; } if (JobModel.GroupingSegments.Count > 1) { try { if (await NotificationProvider.ShowQuestion("Are you sure you want to remove these selected segments?")) { UndoRedoManager.Instance.InsertAndExecuteCommand(new RemoveSegmentsCommand(JobModel)); ArrangeSegmentsIndixes(); DyeCommand.RaiseCanExecuteChanged(); } } catch (Exception ex) { LogManager.Log(ex, "Could not remove segments."); await NotificationProvider.ShowError("An error occurred while trying to delete segments."); } } else { await NotificationProvider.ShowInfo("A job must contain at least one color segment."); } } private void RepeateSegments() { if ( (JobModel.GroupingSegments.ToList().Where(x => x.IsSelected).Count()) < 2) return; UndoRedoManager.Instance.InsertAndExecuteCommand(new RepeatCommand(JobModel)); ArrangeSegmentsIndixes(); } private void UngroupSegments(SegmentsGroupModel segmentsGroup) { UndoRedoManager.Instance.InsertAndExecuteCommand(new UnGroupSegmentsCommand(JobModel, segmentsGroup)); ArrangeSegmentsIndixes(); } private void Paste() { UndoRedoManager.Instance.InsertAndExecuteCommand(new PasteSegmentsCommand(JobModel)); ArrangeSegmentsIndixes(); } private void Copy() { UndoRedoManager.Instance.InsertAndExecuteCommand(new CopySegmentCommand(JobModel)); ArrangeSegmentsIndixes(); } private void Undo() { UndoRedoManager.Instance.Undo(); ArrangeSegmentsIndixes(); DyeCommand.RaiseCanExecuteChanged(); } private void Redo() { UndoRedoManager.Instance.Redo(); ArrangeSegmentsIndixes(); DyeCommand.RaiseCanExecuteChanged(); } #endregion #region Save from models to db private async Task Save() { if (JobModel == null) return; UndoRedoManager.Instance.ClearAll(); var colorSpaces = await _db.ColorSpaces.ToListAsync(); Job.ColorSpace = colorSpaces.FirstOrDefault(); Job.Version = 2; Job.NumberOfUnits = JobModel.NumberOfUnits; Job.EnableInterSegment = JobModel.InterSegmentLength > 0; Job.InterSegmentLength = JobModel.InterSegmentLength; var oldSegments = Job.Segments.ToList(); foreach (var segment in Job.OrderedSegmentsWithGroups.ToList()) { if (segment is Segment) { Segment simplesegment = segment as Segment; simplesegment.BrushStops.ToList().ForEach(x => { _db.BrushStops.Remove(x); }); _db.Segments.Remove(simplesegment); } else if (segment is SegmentsGroup) { SegmentsGroup segmentsGroup = segment as SegmentsGroup; foreach(var groupSegment in segmentsGroup.Segments.ToList()) { groupSegment.BrushStops.ToList().ForEach(x => { _db.BrushStops.Remove(x); }); _db.Segments.Remove(groupSegment); } _db.SegmentsGroups.Remove(segmentsGroup); } } Job.Segments.Clear(); Dictionary<int, SegmentsGroup> segmentIndexToGroup = new Dictionary<int, SegmentsGroup>(); //foreach (var segment in JobModel.Segments.OrderBy(x => x.SegmentIndex).ToList()) foreach(var segment in JobModel.OrderedSegmentsWithGroups) { if(segment is SegmentModel innerSegment) { var dbSegment = new Segment(); dbSegment.Guid = System.Guid.NewGuid().ToString(); dbSegment.Name = "Standard Segment"; dbSegment.Job = Job; dbSegment.SegmentIndex = segment.SegmentIndex; dbSegment.Length = segment.Length; _db.Segments.Add(dbSegment); foreach (var stop in innerSegment.BrushStops.OrderBy(x => x.StopIndex).ToList()) { var dbStop = new BrushStop(); dbStop.Guid = stop.Guid; dbStop.Segment = dbSegment; _db.BrushStops.Add(dbStop); dbStop.ColorSpace = colorSpaces.FirstOrDefault(x => x.Code == (int)stop.ColorSpace); dbStop.Red = stop.Red; dbStop.Green = stop.Green; dbStop.Blue = stop.Blue; dbStop.L = stop.L; dbStop.A = stop.A; dbStop.B = stop.B; dbStop.Cyan = stop.Cyan; dbStop.Magenta = stop.Magenta; dbStop.Yellow = stop.Yellow; dbStop.Black = stop.Black; dbStop.BestMatchR = stop.BestMatchColor.R; dbStop.BestMatchG = stop.BestMatchColor.G; dbStop.BestMatchB = stop.BestMatchColor.B; dbStop.OffsetPercent = stop.OffsetPercent; dbStop.StopIndex = stop.StopIndex; dbStop.IsOutOfGamut = stop.IsOutOfGamut; dbStop.SetVolume(LiquidTypes.Cyan, stop.Cyan); dbStop.SetVolume(LiquidTypes.Magenta, stop.Magenta); dbStop.SetVolume(LiquidTypes.Yellow, stop.Yellow); dbStop.SetVolume(LiquidTypes.Black, stop.Black); dbStop.ColorCatalog = stop.ColorCatalog; dbStop.ColorCatalogsItem = stop.ColorCatalogsItem; } } else if( segment is SegmentsGroupModel group) { SegmentsGroup dbSegmentsGroup = new SegmentsGroup(); dbSegmentsGroup.Guid = System.Guid.NewGuid().ToString(); dbSegmentsGroup.Repeats = group.Repeats; dbSegmentsGroup.SegmentIndex = group.SegmentIndex; dbSegmentsGroup.Job = Job; _db.SegmentsGroups.Add(dbSegmentsGroup); foreach(var segm_group in group.Segments.OrderBy(x=>x.SegmentIndex)) { var dbSegment = new Segment(); dbSegment.Guid = System.Guid.NewGuid().ToString(); dbSegment.Name = "Standard Segment"; dbSegment.Job = Job; dbSegment.SegmentIndex = segm_group.SegmentIndex; dbSegment.Length = segm_group.Length; dbSegment.SegmentsGroupGuid = dbSegmentsGroup.Guid; _db.Segments.Add(dbSegment); foreach (var stop in segm_group.BrushStops.OrderBy(x => x.StopIndex).ToList()) { var dbStop = new BrushStop(); dbStop.Segment = dbSegment; _db.BrushStops.Add(dbStop); dbStop.ColorSpace = colorSpaces.FirstOrDefault(x => x.Code == (int)stop.ColorSpace); dbStop.Red = stop.Red; dbStop.Green = stop.Green; dbStop.Blue = stop.Blue; dbStop.L = stop.L; dbStop.A = stop.A; dbStop.B = stop.B; dbStop.Cyan = stop.Cyan; dbStop.Magenta = stop.Magenta; dbStop.Yellow = stop.Yellow; dbStop.Black = stop.Black; dbStop.BestMatchR = stop.BestMatchColor.R; dbStop.BestMatchG = stop.BestMatchColor.G; dbStop.BestMatchB = stop.BestMatchColor.B; dbStop.OffsetPercent = stop.OffsetPercent; dbStop.StopIndex = stop.StopIndex; dbStop.IsOutOfGamut = stop.IsOutOfGamut; dbStop.SetVolume(LiquidTypes.Cyan, stop.Cyan); dbStop.SetVolume(LiquidTypes.Magenta, stop.Magenta); dbStop.SetVolume(LiquidTypes.Yellow, stop.Yellow); dbStop.SetVolume(LiquidTypes.Black, stop.Black); dbStop.ColorCatalog = stop.ColorCatalog; dbStop.ColorCatalogsItem = stop.ColorCatalogsItem; } } } } Job.LastUpdated = DateTime.UtcNow; Job.IsSynchronized = false; if(Job.JobStatus != JobStatuses.Draft ) { JsonSerializerSettings settings = new JsonSerializerSettings() { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore, PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects }; string jsonJobModelSaved = JsonConvert.SerializeObject(JobModel, Formatting.Indented, settings); var json1 = JObject.Parse(_jsonJobModelLoaded); var json2 = JObject.Parse(jsonJobModelSaved); if(false == JToken.DeepEquals(json1, json2)) Job.JobStatus = BL.Enumerations.JobStatuses.Draft; } RaiseMessage(new JobSavedMessage() { Job = Job }); await _db.SaveChangesAsync(); JsonSerializerSettings settings1 = new JsonSerializerSettings() { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore, PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects }; _jsonJobModelLoaded = JsonConvert.SerializeObject(JobModel, Formatting.Indented, settings1); } #endregion } }