using Serilog;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TINK.Model.Repository.Exception;
using TINK.Model.Repository.Request;
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);
}
}
}
}
}
}