using Serilog; using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using TINK.Repository.Exception; namespace TINK.ViewModel { public class PollingUpdateTask { /// Name of child class provider. private readonly Func m_oGetChildName; /// Object to control canelling. private readonly CancellationTokenSource m_oCanellationTokenSource; /// Task to perform update. private readonly Task m_oUpdateTask; /// Action which performs an update. private readonly Action m_oUpdateAction; /// Obects which does a periodic update. /// Name of the child class. For logging purposes. /// Update action to perform. /// Holds whether to poll or not and the periode leght is polling is on. public PollingUpdateTask( Func p_oGetChildName, Action p_oUpdateAction, TimeSpan? p_oPolling) { m_oGetChildName = p_oGetChildName ?? throw new ArgumentException($"Can not construct {GetType().Name}- obect. Argument {nameof(p_oGetChildName)} must not be null."); m_oUpdateAction = p_oUpdateAction ?? throw new ArgumentException($"Can not construct {GetType().Name}- obect. Argument {nameof(p_oUpdateAction)} must not be null."); if (!p_oPolling.HasValue) { // Automatic update is switched off. Log.ForContext().Debug($"Automatic update is off, context {GetType().Name} at {DateTime.Now}."); return; } var l_iUpdatePeriodeSet = p_oPolling.Value; m_oCanellationTokenSource = new CancellationTokenSource(); int l_iCycleIndex = 2; m_oUpdateTask = Task.Run( async () => { while (!m_oCanellationTokenSource.IsCancellationRequested) { await Task.Delay(l_iUpdatePeriodeSet, m_oCanellationTokenSource.Token); { // N. update cycle Log.ForContext().Information($"Actuating {l_iCycleIndex} update cycle, context {GetType().Name} at {DateTime.Now}."); m_oUpdateAction(); l_iCycleIndex = l_iCycleIndex < int.MaxValue ? ++l_iCycleIndex : 0; } } }, m_oCanellationTokenSource.Token); } /// /// Invoked when pages is closed/ hidden. /// Stops update process. /// public async Task Terminate() { // Cancel update task; if (m_oCanellationTokenSource == null) { throw new Exception($"Can not terminate periodical update task, context {GetType().Name} at {DateTime.Now}. No task running."); } Log.Information($"Request to terminate update cycle, context {GetType().Name} at {DateTime.Now}."); m_oCanellationTokenSource.Cancel(); try { await m_oUpdateTask; } catch (TaskCanceledException) { // Polling update was canceled. // Nothing to notice/ worry about. } catch (Exception l_oException) { // An error occurred updating pin. var l_oAggregateException = l_oException as AggregateException; if (l_oAggregateException == null) { // Unexpected exception detected. Exception should alyways be of type AggregateException Log.Error("An/ several errors occurred on update task. {@Exceptions}.", l_oException); } else { if (l_oAggregateException.InnerExceptions.Count == 1 && l_oAggregateException.InnerExceptions[0].GetType() == typeof(TaskCanceledException)) { // Polling update was canceled. // Nothing to notice/ worry about. } else if (l_oAggregateException.InnerExceptions.Count() > 0 && l_oAggregateException.InnerExceptions.First(x => !x.GetIsConnectFailureException()) == null) // There is no exception which is not of type connect failure. { // All exceptions were caused by communication error Log.Information("An/ several web related connect failure exceptions occurred on update task. {@Exceptions}.", l_oException); } else { // All exceptions were caused by communication errors. Log.Error("An/ several exceptions occurred on update task. {@Exception}.", l_oException); } } } } } }