using System; using System.Collections.Concurrent; using System.Threading.Tasks; using ZeroLevel.Services.Logging; namespace ZeroLevel.Services.Shedulling { internal class AsyncShedullerImpl : IAsyncSheduller { private readonly DateTimeAsyncSheduller _asyncSheduller; public AsyncShedullerImpl() { _asyncSheduller = new DateTimeAsyncSheduller(); } #region One time events public long RemindAsyncAfter(TimeSpan timespan, Func callback) { return _asyncSheduller.Push(timespan, callback); } public long RemindAsyncAt(DateTime date, Func callback) { return _asyncSheduller.Push(date, callback); } #endregion #region Repitable behaviour private readonly ConcurrentDictionary _repitableAsyncActions = new ConcurrentDictionary(); /// /// Исполняет действие раз в период, при этом период перерасчитывается по переданной функции при каждом пересоздании задачи /// /// Функция для расчета следующего периода /// Действие /// Идентификатор задания public long RemindAsyncEveryNonlinearPeriod(Func nextEventPeriodCalcFunction, Func callback, bool breakWherError = false) { return RemindAsyncEveryNonlinearPeriod(nextEventPeriodCalcFunction, nextEventPeriodCalcFunction, callback, breakWherError); } /// /// Исполняет действие раз в период, при этом период перерасчитывается по переданной функции при каждом пересоздании задачи /// /// Функция для расчета периода до первого исполнения /// Функция для расчета периода до последующих исполнений /// Действие /// Идентификатор задания public long RemindAsyncEveryNonlinearPeriod(Func firstEventPeriodCalcFunction, Func nextEventPeriodCalcFunction, Func callback, bool breakWherError = false) { var obj = new ExpiredAsyncObject { ExpirationDate = DateTime.Now.AddMilliseconds(firstEventPeriodCalcFunction().TotalMilliseconds) }; _repitableAsyncActions.TryAdd(obj.Key, obj); obj.Callback = async (key) => { try { await callback(key); } catch (Exception ex) { Log.SystemError(ex, $"[Sheduller] Fault call async task '{key}' handler"); if (breakWherError) return; } ExpiredAsyncObject repObj; if (_repitableAsyncActions.TryGetValue(obj.Key, out repObj)) { _asyncSheduller.Push(repObj.Reset(DateTime.Now.AddMilliseconds(nextEventPeriodCalcFunction().TotalMilliseconds))); } }; return _asyncSheduller.Push(obj); } /// /// Исполняет действие раз в период, при этом дата перерасчитывается по переданной функции при каждом пересоздании задачи /// /// Функция для расчета следующей даты /// Действие /// Идентификатор задания public long RemindAsyncEveryNonlinearDate(Func nextEventDateCalcFunction, Func callback, bool breakWherError = false) { return RemindAsyncEveryNonlinearDate(nextEventDateCalcFunction, nextEventDateCalcFunction, callback, breakWherError); } public long RemindAsyncEveryNonlinearDate(DateTime firstTime, Func nextEventDateCalcFunction, Func callback, bool breakWherError = false) { var obj = new ExpiredAsyncObject { ExpirationDate = firstTime }; _repitableAsyncActions.TryAdd(obj.Key, obj); obj.Callback = async (key) => { try { await callback(key); } catch (Exception ex) { Log.SystemError(ex, $"[Sheduller] Fault call async task '{key}' handler"); if (breakWherError) return; } ExpiredAsyncObject repObj; if (_repitableAsyncActions.TryGetValue(obj.Key, out repObj)) { var nextDate = nextEventDateCalcFunction(obj.ExpirationDate); if (DateTime.Compare(nextDate, DateTime.MinValue) == 0) { Remove(repObj.Key); } else { _asyncSheduller.Push(repObj.Reset(nextDate)); } } }; return _asyncSheduller.Push(obj); } /// /// Исполняет действие раз в период, при этом дата перерасчитывается по переданной функции при каждом пересоздании задачи /// /// Функция для расчет даты первого запуска /// Функция для расчета следующей даты /// Действие /// Идентификатор задания public long RemindAsyncEveryNonlinearDate(Func firstEventDateCalcFunction, Func nextEventDateCalcFunction, Func callback, bool breakWherError = false) { var obj = new ExpiredAsyncObject { ExpirationDate = firstEventDateCalcFunction(DateTime.Now) }; _repitableAsyncActions.TryAdd(obj.Key, obj); obj.Callback = async (key) => { try { await callback(key); } catch (Exception ex) { Log.SystemError(ex, $"[Sheduller] Fault call async task '{key}' handler"); if (breakWherError) return; } ExpiredAsyncObject repObj; if (_repitableAsyncActions.TryGetValue(obj.Key, out repObj)) { var nextDate = nextEventDateCalcFunction(obj.ExpirationDate); if (DateTime.Compare(nextDate, DateTime.MinValue) == 0) { Remove(repObj.Key); } else { _asyncSheduller.Push(repObj.Reset(nextDate)); } } }; return _asyncSheduller.Push(obj); } /// /// Исполняет действие раз в указанный период /// /// Период /// Действие /// Идентификатор задания public long RemindAsyncEvery(TimeSpan timespan, Func callback, bool breakWherError = false) { return RemindAsyncEvery(timespan, timespan, callback, breakWherError); } /// /// Исполняет действие раз в указанный период /// /// Период до первого выполнения /// Период /// Действие /// Идентификатор задания public long RemindAsyncEvery(TimeSpan first, TimeSpan next, Func callback, bool breakWherError = false) { var obj = new ExpiredAsyncObject { ExpirationDate = DateTime.Now.AddMilliseconds(first.TotalMilliseconds) }; _repitableAsyncActions.TryAdd(obj.Key, obj); obj.Callback = async (key) => { try { await callback(key).ConfigureAwait(false); } catch (Exception ex) { Log.SystemError(ex, $"[Sheduller] Fault call async task '{key}' handler"); if (breakWherError) return; } ExpiredAsyncObject repObj; if (_repitableAsyncActions.TryGetValue(obj.Key, out repObj)) { _asyncSheduller.Push(repObj.Reset(DateTime.Now.AddMilliseconds(next.TotalMilliseconds))); } }; return _asyncSheduller.Push(obj); } public long RemindAsyncWhile(TimeSpan period, Func> callback, Action continueWith = null, bool breakWherError = false) { var obj = new ExpiredAsyncObject { ExpirationDate = DateTime.Now.AddMilliseconds(period.TotalMilliseconds) }; _repitableAsyncActions.TryAdd(obj.Key, obj); obj.Callback = async (key) => { bool success = false; try { success = await callback(key); } catch (Exception ex) { Log.SystemError(ex, $"[Sheduller] Fault call async task '{key}' handler"); if (breakWherError) return; } if (success) { Remove(obj.Key); if (continueWith != null) continueWith(); } else { ExpiredAsyncObject repObj; if (_repitableAsyncActions.TryGetValue(obj.Key, out repObj)) { _asyncSheduller.Push(repObj.Reset(DateTime.Now.AddMilliseconds(period.TotalMilliseconds))); } } }; return _asyncSheduller.Push(obj); } #endregion #region Sheduller control public void Pause() { _asyncSheduller.Pause(); } public void Resume() { _asyncSheduller.Resume(); } public void Clean() { _asyncSheduller.Clean(); } public bool Remove(long key) { var success = _asyncSheduller.Remove(key); if (_repitableAsyncActions.ContainsKey(key)) { ExpiredAsyncObject rem; return _repitableAsyncActions.TryRemove(key, out rem); } return success; } #endregion #region IDisposable public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { _asyncSheduller.Dispose(); } } #endregion } }