aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/SideChains/ICSharpCode.AvalonEdit/Search/ISearchStrategy.cs
blob: 5c92993fea45d1d6e7f06339a7e3e31809b92c79 (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
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

using ICSharpCode.AvalonEdit.Document;

namespace ICSharpCode.AvalonEdit.Search
{
	/// <summary>
	/// Basic interface for search algorithms.
	/// </summary>
	public interface ISearchStrategy : IEquatable<ISearchStrategy>
	{
		/// <summary>
		/// Finds all matches in the given ITextSource and the given range.
		/// </summary>
		/// <remarks>
		/// This method must be implemented thread-safe.
		/// All segments in the result must be within the given range, and they must be returned in order
		/// (e.g. if two results are returned, EndOffset of first result must be less than or equal StartOffset of second result).
		/// </remarks>
		IEnumerable<ISearchResult> FindAll(ITextSource document, int offset, int length);
		
		/// <summary>
		/// Finds the next match in the given ITextSource and the given range.
		/// </summary>
		/// <remarks>This method must be implemented thread-safe.</remarks>
		ISearchResult FindNext(ITextSource document, int offset, int length);
	}
	
	/// <summary>
	/// Represents a search result.
	/// </summary>
	public interface ISearchResult : ISegment
	{
		/// <summary>
		/// Replaces parts of the replacement string with parts from the match. (e.g. $1)
		/// </summary>
		string ReplaceWith(string replacement);
	}
	
	/// <summary>
	/// Defines supported search modes.
	/// </summary>
	public enum SearchMode
	{
		/// <summary>
		/// Standard search
		/// </summary>
		Normal,
		/// <summary>
		/// RegEx search
		/// </summary>
		RegEx,
		/// <summary>
		/// Wildcard search
		/// </summary>
		Wildcard
	}
	
	/// <inheritdoc/>
	public class SearchPatternException : Exception, ISerializable
	{
		/// <inheritdoc/>
		public SearchPatternException()
		{
		}
		
		/// <inheritdoc/>
		public SearchPatternException(string message) : base(message)
		{
		}
		
		/// <inheritdoc/>
		public SearchPatternException(string message, Exception innerException) : base(message, innerException)
		{
		}

		// This constructor is needed for serialization.
		/// <inheritdoc/>
		protected SearchPatternException(SerializationInfo info, StreamingContext context) : base(info, context)
		{
		}
	}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.BL.Entities;
using Tango.BL.Enumerations;
using Tango.Core;
using Tango.Logging;
using Tango.PMR.Printing;

namespace Tango.Integration.Operation
{
    /// <summary>
    /// Represents a <see cref="MachineOperator"/> job handler.
    /// </summary>
    /// <seealso cref="Tango.Core.ExtendedObject" />
    public class JobHandler : ExtendedObject
    {
        protected Action _cancelAction;
        protected List<Segment> _effectiveSegments;
        protected String _lastStatusMessage;
        protected int _last_unit;
        protected bool _finalizing;
        protected JobHandlerModes _mode;
        protected double _last_progress;
        protected const int PROGRESS_REPORT_RANGE_METERS = 5;
        protected bool loggedContinueMessage;

        #region Events

        /// <summary>
        /// Occurs when a job status has been received.
        /// </summary>
        public event EventHandler<RunningJobStatus> StatusChanged;

        /// <summary>
        /// Occurs when the job has failed.
        /// </summary>
        public event EventHandler<Exception> Failed;

        /// <summary>
        /// Occurs when the job has completed successfully.
        /// </summary>
        public event EventHandler Completed;

        /// <summary>
        /// Occurs when the job has been canceled.
        /// </summary>
        public event EventHandler Canceled;

        /// <summary>
        /// Occurs when the job has been canceled, failed or completed.
        /// </summary>
        public event EventHandler Stopped;

        /// <summary>
        /// Occurs when rolling the dryer buffer.
        /// </summary>
        public event EventHandler Finalizing;

        /// <summary>
        /// Occurs when a segment has started.
        /// </summary>
        public event EventHandler<Segment> SegmentStarted;

        /// <summary>
        /// Occurs when a segment has completed.
        /// </summary>
        public event EventHandler<Segment> SegmentCompleted;

        /// <summary>
        /// Occurs when a unit completes.
        /// </summary>
        public event EventHandler<int> UnitCompleted;

        /// <summary>
        /// Occurs when the job is set to 1 segment per spool and it's time to replace the spool.
        /// </summary>
        public event EventHandler<SpoolChangeRequiredEventArgs> SpoolChangeRequired;

        /// <summary>
        /// Occurs when <see cref="CanCancel"/> has changed.
        /// </summary>
        public event EventHandler CanCancelChanged;

        #endregion

        #region Properties

        private JobStatus _jobStatus;
        /// <summary>
        /// Gets or sets the current job status that was used to invalidate this handler.
        /// </summary>
        public JobStatus JobStatus
        {
            get { return _jobStatus; }
            set { _jobStatus = value; RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Gets a value indicating whether this handler job has been canceled.
        /// </summary>
        public bool IsCanceled { get; internal set; }

        private bool _canCancel;
        /// <summary>
        /// Gets a value indicating whether the job can be canceled.
        /// </summary>
        public bool CanCancel
        {
            get { return _canCancel; }
            internal set { _canCancel = value; RaisePropertyChangedAuto(); CanCancelChanged?.Invoke(this, new EventArgs()); }
        }

        /// <summary>
        /// Gets the process parameters.
        /// </summary>
        public ProcessParametersTable ProcessParameters { get; private set; }

        private RunningJobStatus _Status;
        /// <summary>
        /// Gets the current status.
        /// </summary>
        public RunningJobStatus Status
        {
            get { return _Status; }
            internal set { _Status = value; RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Gets the job.
        /// </summary>
        public Job Job { get; private set; }

        /// <summary>
        /// Gets the PMR job ticket.
        /// </summary>
        public JobTicket JobTicket { get; private set; }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="JobHandler"/> class.
        /// </summary>
        public JobHandler()
        {
            CanCancel = true;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="JobHandler"/> class.
        /// </summary>
        /// <param name="cancelAction">The cancel action.</param>
        public JobHandler(Action cancelAction, Job job, JobTicket jobTicket, ProcessParametersTable processParameters, JobHandlerModes mode) : this()
        {
            _mode = mode;

            ProcessParameters = processParameters;
            Job = job;
            JobTicket = jobTicket;

            foreach (var s in Job.Segments)
            {
                s.Started = false;
                s.Completed = false;
                s.RemainingTime = TimeSpan.FromSeconds(0);
            }

            _effectiveSegments = Job.EffectiveSegments.ToList();

            _cancelAction = () => { cancelAction(); };

            Status = new RunningJobStatus();


            Status.TotalTime = job.GetEstimatedDuration(processParameters);
            Status.RemainingUnits = job.NumberOfUnits;
            Status.TotalProgress = Job.LengthIncludingNumberOfUnits + processParameters.DryerBufferLengthMeters;
            Status.FinalizingTotalProgress = processParameters.DryerBufferLengthMeters;
            Status.TotalProgressWithoutFinalization = Job.LengthIncludingNumberOfUnits;
            Status.Progress = 0;
            Status.RemainingTime = Status.TotalTime;
            Status.RemainingProgress = Status.TotalProgress;
            Status.CurrentUnitSegments = _effectiveSegments.ToList();
            Status.SettingUpTotalProgress = processParameters.DryerBufferLengthMeters;
            Status.TotalProgressMinusSettingUp = Job.LengthIncludingNumberOfUnits;
            Status.IsSettingUp = true;

            if (mode == JobHandlerModes.Finalization)
            {
                Status.CurrentUnitTotalProgress = Job.Length;
            }
            else
            {
                Status.CurrentUnitTotalProgress = Status.SettingUpTotalProgress;
            }

            if (Job.EnableInterSegment && Job.NumberOfUnits > 1)
            {
                Status.CurrentUnitSegments.Add(Job.CreateInterSegment(Job.InterSegmentLength));
            }

            //Create all segments
            int segment_index = 1;

            for (int j = 0; j < Math.Max(Job.NumberOfUnits, 1); j++)
            {
                for (int i = 0; i < _effectiveSegments.Count; i++)
                {
                    Segment seg = _effectiveSegments[i].Clone();
                    seg.EstimatedDuration = seg.GetEstimatedDuration(processParameters);
                    seg.SegmentIndex = segment_index++;
                    Status.Segments.Add(seg);
                }

                if (Job.EnableInterSegment)
                {
                    var inter_segment = Job.CreateInterSegment(Job.InterSegmentLength);
                    inter_segment.SegmentIndex = segment_index++;
                    Status.Segments.Add(inter_segment);
                }
            }

            var last_segment = Status.Segments.Last();

            if (last_segment.IsInterSegment)
            {
                Status.Segments.Remove(last_segment);
            }

            Status.CurrentSegment = Status.Segments.First();
        }

        #endregion

        #region Internal Methods

        /// <summary>
        /// Raises the status received event.
        /// </summary>
        /// <param name="status">The status.</param>
        public void RaiseStatusReceived(JobStatus status)
        {
            InvalidateJobProgress(status);
        }

        /// <summary>
        /// Raises the failed event.
        /// </summary>
        /// <param name="ex">The ex.</param>
        public void RaiseFailed(Exception ex)
        {
            LogManager.Log($"Job failed at position {Status.Progress}/{Status.TotalProgress}...");
            Status.IsFailed = true;
            RaiseStatusChanged();
            RaisePropertyChanged(nameof(Status));
            Failed?.Invoke(this, ex);
            Stopped?.Invoke(this, new EventArgs());
        }

        /// <summary>
        /// Raises the completed event.
        /// </summary>
        public void RaiseCompleted()
        {
            //This will compensate on any missing progress from Shlomo, but also will tell the wrong progress if job is really completed with a large progress mistake.
            // Might be worth to compensate only on small drifts like the below (ProgressMinusSettingsUp)...
            //InvalidateJobProgress(new JobStatus()
            //{
            //    Progress = Status.TotalProgress,
            //    CurrentSegmentIndex = 0,
            //});

            LogManager.Log($"Job completed at position {Status.Progress}/{Status.TotalProgress}...");

            //If drift is smaller than 10cm auto correct it.
            if (Math.Abs(Status.TotalProgressMinusSettingUp - Status.ProgressMinusSettingUp) < 0.1)
            {
                LogManager.Log($"Job completed with a small drift in the progress minus setting up calculation. ({Status.ProgressMinusSettingUp}/{Status.TotalProgressMinusSettingUp}). Compensating...");
                Status.ProgressMinusSettingUp = Status.TotalProgressMinusSettingUp;
            }
            //If the overall progress is correct? Fix the minus setting up. There is a problem with the delta calc!
            else if (Status.Progress == Status.TotalProgress)
            {
                LogManager.Log($"Job completed with a small drift in the progress minus setting up calculation but the overall progress seems OK. ({Status.ProgressMinusSettingUp}/{Status.TotalProgressMinusSettingUp}). Compensating...");
                Status.ProgressMinusSettingUp = Status.TotalProgressMinusSettingUp;
            }

            Status.Segments.Last().Completed = true;
            Status.RemainingUnits = 0;
            Status.IsFinalizing = false;
            Status.IsCompleted = true;
            RaiseStatusChanged();
            RaisePropertyChanged(nameof(Status));
            Completed?.Invoke(this, new EventArgs());
            Stopped?.Invoke(this, new EventArgs());
        }

        /// <summary>
        /// Raises the canceled event.
        /// </summary>
        public void RaiseCanceled()
        {
            LogManager.Log($"Job canceled at position {Status.Progress}/{Status.TotalProgress}...");
            Status.IsCanceled = true;
            RaiseStatusChanged();
            RaisePropertyChanged(nameof(Status));
            Canceled?.Invoke(this, new EventArgs());
            Stopped?.Invoke(this, new EventArgs());
        }

        /// <summary>
        /// Raises the spool change required event.
        /// </summary>
        internal void RaiseSpoolChangeRequired(Action confirmAction, Action abortAction)
        {
            SpoolChangeRequired?.Invoke(this, new SpoolChangeRequiredEventArgs(confirmAction, abortAction)
            {
                CurrentSegment = Status.CurrentSegment.SegmentIndex,
                TotalSegments = Status.Segments.Count,
            });
        }

        #endregion

        #region Private Methods

        protected virtual void InvalidateJobProgress(JobStatus s)
        {
            JobStatus = s;

            if (_last_progress != s.Progress)
            {
                if (s.Progress <= PROGRESS_REPORT_RANGE_METERS || s.Progress >= Status.TotalProgress - PROGRESS_REPORT_RANGE_METERS)
                {
                    LogManager.Log($"Updating job progress {s.Progress}/{Status.TotalProgress}...");
                }
                else if (!loggedContinueMessage)
                {
                    loggedContinueMessage = true;
                    LogManager.Log($"Progress logging will continue {PROGRESS_REPORT_RANGE_METERS} meters before completion...");
                }
            }

            if (s.Progress < 0)
            {
                LogManager.Log($"Invalid job progress received '{s.Progress}'.", LogCategory.Error);
                return;
            }

            if (s.Progress > Status.TotalProgress)
            {
                LogManager.Log($"Invalid job progress received '{s.Progress}' while total progress is '{Status.TotalProgress}'.", LogCategory.Error);
                return;
            }

            if (s.Progress < _last_progress)
            {
                LogManager.Log($"Invalid job progress received '{s.Progress}' while last progress was '{_last_progress}'.");
            }

            _last_progress = s.Progress;

            List<Segment> unit_segments = new List<Segment>();

            Status.Progress = s.Progress;
            Status.RemainingTime = Status.TotalTime - Job.TranslateProgressToTime(Status.Progress, ProcessParameters);
            Status.RemainingProgress = Status.TotalProgress - Status.Progress;

            if (s.Progress < Status.SettingUpTotalProgress || Status.SettingUpProgress < Status.SettingUpTotalProgress)
            {
                Status.SettingUpProgress = Math.Min(s.Progress, this.Status.SettingUpTotalProgress);
                Status.IsSettingUp = true;
            }
            if (s.Progress >= Status.SettingUpTotalProgress)
            {
                if (Status.IsSettingUp && Status.Progress > 0)
                {
                    Status.IsSettingUp = false;
                }

                Status.ProgressMinusSettingUp = s.Progress - this.Status.SettingUpTotalProgress;
            }

            int units = (int)Math.Max(Job.NumberOfUnits, 1);

            if (s.Progress < Job.LengthIncludingNumberOfUnits || _mode == JobHandlerModes.SettingUp)
            {
                Status.ProgressWithoutFinalization = s.Progress;

                unit_segments = _effectiveSegments.ToList();
                double previousUnitsLengthWithoutThis = 0.0;

                int currentUnit = Status.CurrentUnit;
                double currentUnitProgress = Status.CurrentUnitProgress;
                double jobLength = Job.Length;

                for (int index = 0; index < units; ++index)
                {
                    currentUnit = index;
                    double unitLength = !Job.EnableInterSegment || index >= units - 1 ? jobLength : jobLength + Job.InterSegmentLength;
                    if (_mode == JobHandlerModes.Finalization)
                    {
                        if (s.Progress < unitLength + previousUnitsLengthWithoutThis)
                        {
                            currentUnitProgress = s.Progress - previousUnitsLengthWithoutThis;
                            break;
                        }
                    }
                    else if (s.Progress <= previousUnitsLengthWithoutThis + unitLength + Status.SettingUpProgress)
                    {
                        if (!Status.IsSettingUp)
                        {
                            currentUnitProgress = s.Progress - previousUnitsLengthWithoutThis - this.Status.SettingUpProgress;
                            break;
                        }
                        break;
                    }
                    previousUnitsLengthWithoutThis += unitLength;
                }

                Status.CurrentUnit = currentUnit;
                Status.CurrentUnitProgress = currentUnitProgress;

                Status.RemainingUnits = this.Job.NumberOfUnits - this.Status.CurrentUnit;

                if (Job.EnableInterSegment && Job.NumberOfUnits > 1 && Status.RemainingUnits > 1)
                {
                    unit_segments.Add(Job.CreateInterSegment(Job.InterSegmentLength));
                }

                if (unit_segments.Count != Status.CurrentUnitSegments.Count)
                {
                    Status.CurrentUnitSegments = unit_segments;
                }

                Status.CurrentUnitTotalProgress = Status.RemainingUnits > 1 && Job.EnableInterSegment ? Job.Length + (Job.InterSegmentLength) : Job.Length;

                if (s.Message != _lastStatusMessage )
                {
                    Status.Message = s.Message;
                }
               
                _lastStatusMessage = s.Message;


                RaiseStatusChanged();

                //Segments Completion
                if (Status.CurrentUnit > _last_unit)
                {
                    foreach (var segment in Status.CurrentUnitSegments)
                    {
                        segment.Started = false;
                        segment.Completed = false;
                    }

                    if (Job.NumberOfUnits > 1)
                    {
                        RaiseUnitCompleted(_last_unit);
                    }
                }

                _last_unit = Status.CurrentUnit;


                for (int i = 0; i < Status.CurrentUnitSegments.Count; i++)
                {
                    Segment segment = Status.CurrentUnitSegments[i];
                    double previousSegmentsLengthWithThis = Status.CurrentUnitSegments.Take(i + 1).Sum(x => x.LengthWithFactor);
                    TimeSpan segmentsDuration = Job.TranslateProgressToTime(previousSegmentsLengthWithThis, ProcessParameters);
                    TimeSpan segmentRemainingTime = segmentsDuration - Job.TranslateProgressToTime(Status.CurrentUnitProgress, ProcessParameters);

                    if (i == 0 && Status.CurrentUnitProgress > 0)
                    {
                        if (!segment.Started)
                        {
                            segment.Started = true;
                            RaiseSegmentStarted(segment);
                        }
                    }

                    if (Status.CurrentUnitProgress >= previousSegmentsLengthWithThis)
                    {
                        if (!segment.Completed)
                        {
                            segment.Completed = true;
                            RaiseSegmentCompleted(segment);
                        }

                        if (i < Status.CurrentUnitSegments.Count - 1)
                        {
                            if (!Status.CurrentUnitSegments[i + 1].Started)
                            {
                                Status.CurrentUnitSegments[i + 1].Started = true;
                                RaiseSegmentStarted(Status.CurrentUnitSegments[i + 1]);
                            }
                        }
                    }

                    if (segment.Started && !segment.Completed)
                    {
                        segment.RemainingTime = segmentRemainingTime;
                    }
                }

                //Set Segment Completion for All Segments List
                for (int i = 0; i < Status.Segments.Count; i++)
                {
                    Segment segment = Status.Segments[i];
                    double previousSegmentsLengthWithThis = Status.Segments.Take(i + 1).Sum(x => x.LengthWithFactor);
                    TimeSpan segmentsDuration = Job.TranslateProgressToTime(previousSegmentsLengthWithThis, ProcessParameters);
                    TimeSpan segmentRemainingTime = segmentsDuration - Job.TranslateProgressToTime(Status.Progress, ProcessParameters);

                    // segment.Progress = Math.Min(Math.Max((previousSegmentsLengthWithThis - segment.Length - Status.Progress) * -1, 0), segment.Length);
                    segment.Progress = Math.Min(Math.Max((previousSegmentsLengthWithThis - segment.Length - Status.ProgressMinusSettingUp) * -1, 0), segment.Length);

                    if (i == 0 && Status.Progress > 0)
                    {
                        if (!segment.Started)
                        {
                            segment.Started = true;
                            Status.CurrentSegment = segment;
                        }
                    }
                    
                    //if (Status.Progress >= previousSegmentsLengthWithThis)
                    if (Status.ProgressMinusSettingUp >= previousSegmentsLengthWithThis)
                    {
                        if (!segment.Completed)
                        {
                            segment.Completed = true;
                        }

                        if (i < Status.Segments.Count - 1)
                        {
                            if (!Status.Segments[i + 1].Started)
                            {
                                Status.Segments[i + 1].Started = true;
                                Status.CurrentSegment = Status.Segments[i + 1];
                            }
                        }
                    }

                    if (segment.Started && !segment.Completed)
                    {
                        segment.RemainingTime = segmentRemainingTime;
                    }
                }
            }
            else
            {
                //Finalizing
                if (!_finalizing)
                {
                    _finalizing = true;
                    Status.IsFinalizing = true;
                    var last_Segment = _effectiveSegments.Last().Clone();
                    last_Segment.Length = ProcessParameters.DryerBufferLengthMeters;
                    Status.CurrentUnitSegments = new List<Segment> { last_Segment };
                    Status.CurrentUnitTotalProgress = last_Segment.Length;
                    Status.CurrentUnitProgress = 0;
                    Status.ProgressWithoutFinalization = Status.TotalProgressWithoutFinalization;
                    RaiseFinalizing();
                }

                Status.CurrentUnitProgress = s.Progress - Job.LengthIncludingNumberOfUnits;
                Status.FinalizingProgress = s.Progress - Status.TotalProgressWithoutFinalization;
            }
        }

        #endregion

        #region Protected Methods

        protected void RaiseStatusChanged()
        {
            StatusChanged?.Invoke(this, Status);
        }

        protected void RaiseSegmentStarted(Segment segment)
        {
            if (segment.IsInterSegment)
            {
                LogManager.Log($"Inter Segment started.");
            }
            else
            {
                LogManager.Log($"Segment {segment.SegmentIndex} of unit {Status.CurrentUnit + 1} started...");
            }

            SegmentStarted?.Invoke(this, segment);
        }

        protected void RaiseSegmentCompleted(Segment segment)
        {
            if (segment.IsInterSegment)
            {
                LogManager.Log($"Inter Segment completed.");
            }
            else
            {
                LogManager.Log($"Segment {segment.SegmentIndex} of unit {Status.CurrentUnit + 1} completed.");
            }
            SegmentCompleted?.Invoke(this, segment);
        }

        protected void RaiseUnitCompleted(int unit)
        {
            LogManager.Log($"Unit {unit + 1} completed...");
            UnitCompleted?.Invoke(this, unit);
        }

        protected void RaiseFinalizing()
        {
            LogManager.Log($"Finalizing...");
            Finalizing?.Invoke(this, new EventArgs());
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Cancels the associated job.
        /// </summary>
        public void Cancel()
        {
            _cancelAction();
        }

        #endregion
    }
}