IPCamAppBar/IPCamLib/RetryableCamera.cs

83 lines
2.7 KiB
C#

using System;
using System.Threading;
using System.Threading.Tasks;
// ReSharper disable UnusedMember.Global - public API
namespace IPCamLib
{
/// <summary>
/// Receives frames from an ICamera as well as status updates.
/// </summary>
public interface IRetryableCameraObserver : ICameraObserver
{
/// <summary>
/// Called when a new Fetch attempt has started.
/// </summary>
Task OnFetch();
/// <summary>
/// Called when the stream was disconnected.
/// </summary>
/// <param name="exception">Contains the exception, if any occured. Null for graceful disconnects and timeouts.</param>
/// <param name="retryDelay">The delay until the next Fetch is attempted.</param>
Task OnDisconnected(Exception exception, TimeSpan retryDelay);
}
/// <summary>
/// Implements retry logic for an ICamera instance.
/// </summary>
public class RetryableCamera
{
private readonly ICamera camera;
/// <summary>
/// Creates a new instance of a RetryableCamera.
/// </summary>
/// <param name="camera">The camera instance to fetch from.</param>
public RetryableCamera(ICamera camera)
{
this.camera = camera;
}
/// <summary>
/// Starts receiving frames and will retry when disconnected.
/// </summary>
/// <param name="observer">The observer implementation to receive frames and status updates.</param>
/// <param name="cancellationToken">A CancellationToken which will stop the camera stream when cancelled.</param>
public async Task Fetch(IRetryableCameraObserver observer, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
// TODO incremental back-off?
var retryDelay = TimeSpan.FromSeconds(5);
var exception = false;
try
{
await observer.OnFetch();
await camera.Fetch(observer, cancellationToken);
}
catch (TaskCanceledException)
{
// Empty by design
}
catch (Exception e)
{
await observer.OnDisconnected(e, retryDelay);
exception = true;
}
if (!exception && !cancellationToken.IsCancellationRequested)
await observer.OnDisconnected(null, retryDelay);
await Task.Delay(retryDelay, cancellationToken);
}
}
}
}