using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Disco.Services.Tasks { using ChangedItem = KeyValuePair; public class ScheduledTaskStatus : IScheduledTaskStatus { #region Backing Fields private string _sessionId; private string _triggerKey; private string _taskName; private Type _taskType; private TaskCompletionSource _tcs; private bool _isSilent; private byte _progress; private string _currentProcess; private string _currentDescription; private Exception _taskException; private bool _cancelInitiallySupported; private bool _cancelSupported; private bool _isCanceling; private DateTime? _startedTimestamp; private DateTime? _nextScheduledTimestamp; private DateTime? _finishedTimestamp; private string _finishedMessage; private string _finishedUrl; private int _statusVersion = 0; #endregion #region Properties public string SessionId { get { return _sessionId; } } public string TriggerKey { get { return _triggerKey; } } public string TaskName { get { return _taskName; } } public Type TaskType { get { return _taskType; } } public bool IsSilent { get { return _isSilent; } } public byte Progress { get { return _progress; } } public string CurrentProcess { get { return _currentProcess; } } public string CurrentDescription { get { return _currentDescription; } } public bool IgnoreCurrentProcessChanges { get; set; } public bool IgnoreCurrentDescription { get; set; } public double ProgressMultiplier { get; set; } public byte ProgressOffset { get; set; } public Exception TaskException { get { return _taskException; } } public bool CancelSupported { get { return _cancelSupported; } } public bool IsCanceling { get { return _isCanceling; } } public DateTime? StartedTimestamp { get { return _startedTimestamp; } } public DateTime? FinishedTimestamp { get { return _finishedTimestamp; } } public DateTime? NextScheduledTimestamp { get { return _nextScheduledTimestamp; } } public string FinishedMessage { get { return _finishedMessage; } } public string FinishedUrl { get { return _finishedUrl; } } public int StatusVersion { get { return _statusVersion; } } public bool IsRunning { get { return _startedTimestamp.HasValue && !_finishedTimestamp.HasValue; } } public Task CompletionTask { get { return _tcs.Task; } } #endregion #region Events public delegate void UpdatedEvent(ScheduledTaskStatus sender, KeyValuePair[] ChangedProperties); public delegate void CancelingEvent(ScheduledTaskStatus sender); public event UpdatedEvent Updated; public event CancelingEvent Canceling; #endregion public ScheduledTaskStatus(ScheduledTask Task, string SessionId, string TriggerKey, string FinishedUrl = null) { _taskName = Task.TaskName; _taskType = Task.GetType(); _tcs = new TaskCompletionSource(); _sessionId = SessionId; _triggerKey = TriggerKey; _cancelInitiallySupported = Task.CancelInitiallySupported; _cancelSupported = _cancelInitiallySupported; _finishedUrl = FinishedUrl; _currentProcess = "Scheduled"; _currentDescription = "Scheduled Task for Execution"; ProgressMultiplier = 1; _progress = 0; } public void LogWarning(string Message) { ScheduledTasksLog.LogScheduledTaskWarning(TaskName, SessionId, Message); } public void LogInformation(string Message) { ScheduledTasksLog.LogScheduledTaskInformation(TaskName, SessionId, Message); } #region Progress Actions private byte CalculateProgressValue(byte Progress) { return (byte)((Progress * ProgressMultiplier) + ProgressOffset); } public void UpdateStatus(byte Progress) { _progress = CalculateProgressValue(Progress); UpdateTriggered("Progress", _progress); } public void UpdateStatus(double Progress) { UpdateStatus((byte)Progress); } public void UpdateStatus(string CurrentDescription) { if (!IgnoreCurrentDescription) { _currentDescription = CurrentDescription; UpdateTriggered("CurrentDescription", _currentDescription); } } public void UpdateStatus(byte Progress, string CurrentDescription) { _progress = CalculateProgressValue(Progress); var changedProperties = new List() { new ChangedItem("Progress", Progress) }; if (!IgnoreCurrentDescription) { _currentDescription = CurrentDescription; changedProperties.Add(new ChangedItem("CurrentDescription", CurrentDescription)); } UpdateTriggered(changedProperties.ToArray()); } public void UpdateStatus(double Progress, string CurrentDescription) { UpdateStatus((byte)Progress, CurrentDescription); } public void UpdateStatus(byte Progress, string CurrentProcess, string CurrentDescription) { _progress = CalculateProgressValue(Progress); var changedProperties = new List() { new ChangedItem("Progress", Progress) }; if (!IgnoreCurrentProcessChanges) { _currentProcess = CurrentProcess; changedProperties.Add(new ChangedItem("CurrentProcess", CurrentProcess)); } if (!IgnoreCurrentDescription) { _currentDescription = CurrentDescription; changedProperties.Add(new ChangedItem("CurrentDescription", CurrentDescription)); } UpdateTriggered(changedProperties.ToArray()); } public void UpdateStatus(double Progress, string CurrentProcess, string CurrentDescription) { UpdateStatus((byte)Progress, CurrentProcess, CurrentDescription); } #endregion #region State Actions public bool Canceled() { if (!_isCanceling) { if (_cancelSupported) { // Cancelling _isCanceling = true; UpdateTriggered("IsCancelling", true); if (Canceling != null) Canceling(this); return true; } else { // Cancelling not supported return false; } } else { // Already Cancelling return true; } } public void SetCancelSupported(bool CancelSupported) { if (_cancelSupported != CancelSupported) { _cancelSupported = CancelSupported; UpdateTriggered("CancelSupported", CancelSupported); } } public void SetTaskException(Exception TaskException) { if (_taskException != TaskException) { _taskException = TaskException; UpdateTriggered("TaskExceptionMessage", (_taskException == null ? null : _taskException.Message)); } } public void SetIsSilent(bool IsSilent) { if (_isSilent != IsSilent) _isSilent = IsSilent; } public void SetFinishedUrl(string FinishedUrl) { if (_finishedUrl != FinishedUrl) { _finishedUrl = FinishedUrl; UpdateTriggered("FinishedUrl", FinishedUrl); } } public void SetFinishedMessage(string FinishedMessage) { if (_finishedMessage != FinishedMessage) { _finishedMessage = FinishedMessage; UpdateTriggered("FinishedMessage", FinishedMessage); } } public void SetNextScheduledTimestamp(DateTime? NextScheduledTimestamp) { if (_nextScheduledTimestamp != NextScheduledTimestamp) { _nextScheduledTimestamp = NextScheduledTimestamp; UpdateTriggered("NextScheduledTimestamp", NextScheduledTimestamp); } } public void Started() { var changedProperties = new List(); // Change StartedTimestamp _startedTimestamp = DateTime.Now; changedProperties.Add(new ChangedItem("StartedTimestamp", StartedTimestamp)); if (_finishedTimestamp != null) { _finishedTimestamp = null; changedProperties.Add(new ChangedItem("FinishedTimestamp", _finishedTimestamp)); } if (_nextScheduledTimestamp != null) { _nextScheduledTimestamp = null; changedProperties.Add(new ChangedItem("NextScheduledTimestamp", _nextScheduledTimestamp)); } changedProperties.Add(new ChangedItem("IsRunning", IsRunning)); if (_progress != 0) { _progress = 0; changedProperties.Add(new ChangedItem("Progress", _progress)); } if (_currentProcess != "Starting") { _currentProcess = "Starting"; changedProperties.Add(new ChangedItem("CurrentProcess", _currentProcess)); } if (_currentDescription != "Initializing Task for Execution") { _currentDescription = "Initializing Task for Execution"; changedProperties.Add(new ChangedItem("CurrentDescription", _currentDescription)); } if (_taskException != null) { _taskException = null; changedProperties.Add(new ChangedItem("TaskExceptionMessage", (_taskException == null ? null : _taskException.Message))); } if (_cancelSupported != _cancelInitiallySupported) { _cancelSupported = _cancelInitiallySupported; changedProperties.Add(new ChangedItem("CancelSupported", _cancelSupported)); } if (_isCanceling) { _isCanceling = false; changedProperties.Add(new ChangedItem("IsCanceling", _isCanceling)); } UpdateTriggered(changedProperties.ToArray()); } public void Finished() { Finished(_finishedMessage, _finishedUrl); } public void Finished(string FinishedMessage) { Finished(FinishedMessage, _finishedUrl); } public void Finished(string FinishedMessage, string FinishedUrl) { var changedProperties = new List(); _finishedTimestamp = DateTime.Now; changedProperties.Add(new ChangedItem("FinishedTimestamp", _finishedTimestamp)); changedProperties.Add(new ChangedItem("IsRunning", IsRunning)); if (FinishedMessage != _finishedMessage) { _finishedMessage = FinishedMessage; changedProperties.Add(new ChangedItem("FinishedMessage", _finishedMessage)); } if (FinishedUrl != _finishedUrl) { _finishedUrl = FinishedUrl; changedProperties.Add(new ChangedItem("FinishedUrl", _finishedUrl)); } if (_isCanceling) { _isCanceling = false; changedProperties.Add(new ChangedItem("IsCanceling", _isCanceling)); } UpdateTriggered(changedProperties.ToArray()); } internal void Finally() { _tcs.SetResult(this); } public void Reset(DateTime? NextScheduledTimestamp) { if (_tcs != null) _tcs.Task.Dispose(); _tcs = new TaskCompletionSource(); var changedProperties = new List(); if (_nextScheduledTimestamp != NextScheduledTimestamp) { _nextScheduledTimestamp = NextScheduledTimestamp; changedProperties.Add(new ChangedItem("NextScheduledTimestamp", _nextScheduledTimestamp)); } if (_startedTimestamp != null) { _startedTimestamp = null; changedProperties.Add(new ChangedItem("StartedTimestamp", _startedTimestamp)); } if (_finishedTimestamp != null) { _finishedTimestamp = null; changedProperties.Add(new ChangedItem("FinishedTimestamp", _finishedTimestamp)); } if (_finishedMessage != null) { _finishedMessage = null; changedProperties.Add(new ChangedItem("FinishedMessage", _finishedMessage)); } if (_finishedUrl != null) { _finishedUrl = null; changedProperties.Add(new ChangedItem("FinishedUrl", _finishedUrl)); } if (_progress != 0) { _progress = 0; changedProperties.Add(new ChangedItem("Progress", _progress)); } ProgressMultiplier = 1; ProgressOffset = 0; IgnoreCurrentDescription = false; IgnoreCurrentProcessChanges = false; if (_currentProcess != "Scheduled") { _currentProcess = "Scheduled"; changedProperties.Add(new ChangedItem("CurrentProcess", _currentProcess)); } if (_currentDescription != "Scheduled Task for Execution") { _currentDescription = "Scheduled Task for Execution"; changedProperties.Add(new ChangedItem("CurrentDescription", _currentDescription)); } if (_isCanceling) { _isCanceling = false; changedProperties.Add(new ChangedItem("IsCanceling", _isCanceling)); } UpdateTriggered(changedProperties.ToArray()); } public bool WaitUntilFinished(TimeSpan Timeout) { var finished = _tcs.Task.Wait(Timeout); // Return false if task completed, but with an error if (finished) return TaskException == null; else return false; } #endregion private void UpdateTriggered(string ChangedProperty, object NewValue) { UpdateTriggered(new ChangedItem[] { new ChangedItem(ChangedProperty, NewValue) }); } private void UpdateTriggered(params ChangedItem[] ChangedProperties) { _statusVersion++; if (Updated != null) Updated(this, ChangedProperties); if (!_isSilent) ScheduledTaskNotificationsHub.PublishEvent(SessionId, ChangedProperties); } } }