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.Core;
using Tango.Logging;
using Tango.PMR.Printing;
namespace Tango.Integration.Operation
{
///
/// Represents a job handler.
///
///
public class JobHandler : ExtendedObject
{
private Action _cancelAction;
private List _effectiveSegments;
private String _lastStatusMessage;
private int _last_unit;
private bool _finalizing;
private JobHandlerModes _mode;
private double _last_progress;
#region Events
///
/// Occurs when a job status has been received.
///
public event EventHandler StatusChanged;
///
/// Occurs when the job has failed.
///
public event EventHandler Failed;
///
/// Occurs when the job has completed successfully.
///
public event EventHandler Completed;
///
/// Occurs when the job has been canceled.
///
public event EventHandler Canceled;
///
/// Occurs when the job has been canceled, failed or completed.
///
public event EventHandler Stopped;
///
/// Occurs when rolling the dryer buffer.
///
public event EventHandler Finalizing;
///
/// Occurs when a segment has started.
///
public event EventHandler SegmentStarted;
///
/// Occurs when a segment has completed.
///
public event EventHandler SegmentCompleted;
///
/// Occurs when a unit completes.
///
public event EventHandler UnitCompleted;
///
/// Occurs when the job is set to 1 segment per spool and it's time to replace the spool.
///
public event EventHandler SpoolChangeRequired;
#endregion
#region Properties
///
/// Gets a value indicating whether this handler job has been canceled.
///
public bool IsCanceled { get; internal set; }
///
/// Gets the process parameters.
///
public ProcessParametersTable ProcessParameters { get; private set; }
private RunningJobStatus _Status;
///
/// Gets the current status.
///
public RunningJobStatus Status
{
get { return _Status; }
internal set { _Status = value; RaisePropertyChangedAuto(); }
}
///
/// Gets the job.
///
public Job Job { get; private set; }
///
/// Gets the PMR job ticket.
///
public JobTicket JobTicket { get; private set; }
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
public JobHandler()
{
}
///
/// Initializes a new instance of the class.
///
/// The cancel action.
internal 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 = () => { IsCanceled = true; 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 < Job.NumberOfUnits; 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
///
/// Raises the status received event.
///
/// The status.
internal void RaiseStatusReceived(JobStatus status)
{
InvalidateJobProgress(status);
}
///
/// Raises the failed event.
///
/// The ex.
internal void RaiseFailed(Exception ex)
{
Status.IsFailed = true;
StatusChanged?.Invoke(this, Status);
RaisePropertyChanged(nameof(Status));
Failed?.Invoke(this, ex);
Stopped?.Invoke(this, new EventArgs());
}
///
/// Raises the completed event.
///
internal void RaiseCompleted()
{
Status.Segments.Last().Completed = true;
Status.RemainingUnits = 0;
Status.IsFinalizing = false;
Status.IsCompleted = true;
StatusChanged?.Invoke(this, Status);
RaisePropertyChanged(nameof(Status));
Completed?.Invoke(this, new EventArgs());
Stopped?.Invoke(this, new EventArgs());
}
///
/// Raises the canceled event.
///
internal void RaiseCanceled()
{
Status.IsCanceled = true;
StatusChanged?.Invoke(this, Status);
RaisePropertyChanged(nameof(Status));
Canceled?.Invoke(this, new EventArgs());
Stopped?.Invoke(this, new EventArgs());
}
///
/// Raises the spool change required event.
///
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
private void InvalidateJobProgress(JobStatus s)
{
bool invalidProgress = false;
LogManager.Log($"Updating job progress {s.Progress}/{Status.TotalProgress}...");
if (s.Progress < 0)
{
LogManager.Log($"Invalid job progress received '{s.Progress}'.", LogCategory.Error);
invalidProgress = true;
}
if (s.Progress > Status.TotalProgress)
{
LogManager.Log($"Invalid job progress received '{s.Progress}' while total progress is '{Status.TotalProgress}'.", LogCategory.Error);
invalidProgress = true;
}
if (s.Progress < _last_progress)
{
LogManager.Log($"Invalid job progress received '{s.Progress}' while last progress was '{_last_progress}'.");
invalidProgress = true;
}
if (invalidProgress)
{
return;
}
_last_progress = s.Progress;
//Job Status
if (IsCanceled)
{
Status.IsCanceled = IsCanceled;
StatusChanged?.Invoke(this, Status);
return;
}
List unit_segments = new List();
double delta = s.Progress - Status.Progress;
Status.Progress = s.Progress;
Status.RemainingTime = Status.TotalTime - Job.TranslateProgressToTime(Status.Progress, ProcessParameters);
Status.RemainingProgress = Status.TotalProgress - Status.Progress;
if (Status.SettingUpProgress < Status.SettingUpTotalProgress)
{
Status.SettingUpProgress += delta;
}
else
{
if (Status.IsSettingUp && Status.Progress > 0)
{
Status.IsSettingUp = false;
}
Status.ProgressMinusSettingUp += delta;
}
if (s.Progress < Job.LengthIncludingNumberOfUnits || _mode == JobHandlerModes.SettingUp)
{
Status.ProgressWithoutFinalization += delta;
unit_segments = _effectiveSegments.ToList();
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 (_mode == JobHandlerModes.Finalization)
{
Status.CurrentUnitProgress += delta;
}
else
{
if (!Status.IsSettingUp)
{
Status.CurrentUnitProgress += delta;
}
}
if (Status.CurrentUnitProgress >= Status.CurrentUnitTotalProgress)
{
Status.CurrentUnitProgress = 0;
Status.CurrentUnit++;
}
Status.RemainingUnits = Job.NumberOfUnits - Status.CurrentUnit;
if (s.Message != _lastStatusMessage && s.Message != String.Empty)
{
Status.Message = s.Message;
}
else
{
Status.Message = null;
}
_lastStatusMessage = s.Message;
StatusChanged?.Invoke(this, Status);
//Segments Completion
if (Status.CurrentUnit > _last_unit)
{
foreach (var segment in Status.CurrentUnitSegments)
{
segment.Started = false;
segment.Completed = false;
}
if (Job.NumberOfUnits > 1)
{
UnitCompleted?.Invoke(this, _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;
SegmentStarted?.Invoke(this, segment);
}
}
if (Status.CurrentUnitProgress >= previousSegmentsLengthWithThis)
{
if (!segment.Completed)
{
segment.Completed = true;
SegmentCompleted?.Invoke(this, segment);
}
if (i < Status.CurrentUnitSegments.Count - 1)
{
if (!Status.CurrentUnitSegments[i + 1].Started)
{
Status.CurrentUnitSegments[i + 1].Started = true;
SegmentStarted?.Invoke(this, 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);
if (i == 0 && Status.Progress > 0)
{
if (!segment.Started)
{
segment.Started = true;
Status.CurrentSegment = segment;
}
}
if (Status.Progress >= 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 { last_Segment };
Status.CurrentUnitTotalProgress = last_Segment.Length;
Status.CurrentUnitProgress = 0;
Status.ProgressWithoutFinalization = Status.TotalProgressWithoutFinalization;
Finalizing?.Invoke(this, new EventArgs());
}
Status.CurrentUnitProgress += delta;
Status.FinalizingProgress += delta;
}
}
#endregion
#region Public Methods
///
/// Cancels the associated job.
///
public void Cancel()
{
_cancelAction();
}
#endregion
}
}