sharee.bike-App/TINKLib/ViewModel/PollingUpdateTask.cs

132 lines
4.1 KiB
C#
Raw Normal View History

2023-04-19 12:14:14 +02:00
using System;
2021-05-13 20:03:07 +02:00
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
2022-08-30 15:42:25 +02:00
using Serilog;
2021-06-26 20:57:55 +02:00
using TINK.Repository.Exception;
2021-05-13 20:03:07 +02:00
namespace TINK.ViewModel
{
2022-09-06 16:08:19 +02:00
/// <summary>
/// Object to periodically actuate an action.
/// </summary>
public class PollingUpdateTask
{
2023-06-06 12:00:24 +02:00
/// <summary> Object to control canceling. </summary>
private CancellationTokenSource CancellationTokenSource { get; }
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
/// <summary> Task to perform update. </summary>
private Task UpdateTask { get; }
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
/// <summary> Action which performs an update.</summary>
private Action UpdateAction { get; }
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
/// <summary> Obects which does a periodic update. </summary>
/// <param name="updateAction">Update action to perform.</param>
2023-04-19 12:14:14 +02:00
/// <param name="polling"> Holds whether to poll or not and the period length is polling is on. </param>
2022-09-06 16:08:19 +02:00
public PollingUpdateTask(
Action updateAction,
TimeSpan? polling)
{
UpdateAction = updateAction
?? throw new ArgumentException($"Can not construct {GetType().Name}- obect. Argument {nameof(updateAction)} must not be null.");
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
if (!polling.HasValue)
{
// Automatic update is switched off.
Log.ForContext<PollingUpdateTask>().Debug($"Automatic update is off, context {GetType().Name} at {DateTime.Now}.");
return;
}
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
var updatePeriodeSet = polling.Value;
2021-05-13 20:03:07 +02:00
2023-06-06 12:00:24 +02:00
CancellationTokenSource = new CancellationTokenSource();
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
int cycleIndex = 2;
UpdateTask = Task.Run(
async () =>
{
2023-06-06 12:00:24 +02:00
while (!CancellationTokenSource.IsCancellationRequested)
2022-09-06 16:08:19 +02:00
{
2023-06-06 12:00:24 +02:00
await Task.Delay(updatePeriodeSet, CancellationTokenSource.Token);
2022-09-06 16:08:19 +02:00
{
// N. update cycle
Log.ForContext<PollingUpdateTask>().Information($"Actuating {cycleIndex} update cycle, context {GetType().Name} at {DateTime.Now}.");
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
UpdateAction();
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
Log.ForContext<PollingUpdateTask>().Debug($"Update cycle {cycleIndex}, context {GetType().Name} finised at {DateTime.Now}.");
cycleIndex = cycleIndex < int.MaxValue ? ++cycleIndex : 0;
}
}
},
2023-06-06 12:00:24 +02:00
CancellationTokenSource.Token);
2022-09-06 16:08:19 +02:00
}
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
/// <summary>
/// Invoked when pages is closed/ hidden.
/// Stops update process.
/// </summary>
public async Task Terminate()
{
if (UpdateTask == null)
{
// Nothing to do if no task was created.
return;
}
2022-06-17 14:17:58 +02:00
2022-09-06 16:08:19 +02:00
// Cancel update task;
2023-06-06 12:00:24 +02:00
if (CancellationTokenSource == null)
2022-09-06 16:08:19 +02:00
{
throw new Exception($"Can not terminate periodical update task, context {GetType().Name} at {DateTime.Now}. No task running.");
}
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
Log.ForContext<PollingUpdateTask>().Information($"Request to terminate update cycle, context {GetType().Name} at {DateTime.Now}.");
2023-06-06 12:00:24 +02:00
CancellationTokenSource.Cancel();
2021-05-13 20:03:07 +02:00
2022-09-06 16:08:19 +02:00
try
{
await UpdateTask;
}
catch (TaskCanceledException)
{
// Polling update was canceled.
// Nothing to notice/ worry about.
}
catch (Exception exception)
{
// An error occurred updating pin.
var aggregateException = exception as AggregateException;
if (aggregateException == null)
{
2023-11-21 15:26:57 +01:00
// Unexpected exception detected. Exception should always be of type AggregateException
2022-09-06 16:08:19 +02:00
Log.Error("An/ several errors occurred on update task. {@Exceptions}.", exception);
}
else
{
if (aggregateException.InnerExceptions.Count == 1
&& aggregateException.InnerExceptions[0].GetType() == typeof(TaskCanceledException))
{
// Polling update was canceled.
// Nothing to notice/ worry about.
Log.ForContext<PollingUpdateTask>().Debug($"Update cycle terminated.");
}
else if (aggregateException.InnerExceptions.Count() > 0 &&
aggregateException.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}.", exception);
}
else
{
// All exceptions were caused by communication errors.
Log.Error("An/ several exceptions occurred on update task. {@Exception}.", exception);
}
}
}
Log.ForContext<PollingUpdateTask>().Debug($"Update cycle terminated (not canceled).");
}
}
2021-05-13 20:03:07 +02:00
}