using OpenMacroBoard.SDK.Internals;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
#nullable enable
namespace OpenMacroBoard.SDK
{
///
/// A collection of extensions for .
///
public static class DeviceContextExtensions
{
///
/// Wait for and open the first detected in this context.
///
///
///
/// If there are multiple devices connected it's undefined which one you will get.
/// If you want to specify a filter to get a specific device use
/// .
///
///
public static Task OpenAsync(
this IDeviceContext context,
CancellationToken cancellationToken = default
)
{
return context.OpenAsync(_ => true, cancellationToken);
}
///
/// Wait for and open the first matching in this context.
///
public static async Task OpenAsync(
this IDeviceContext context,
Func selector,
CancellationToken cancellationToken = default
)
{
return (await context.GetDeviceReferenceAsync(selector, cancellationToken)).Open();
}
///
/// Wait for and return the first matching .
///
public static async Task GetDeviceReferenceAsync(
this IDeviceContext context,
Func selector,
CancellationToken cancellationToken = default
)
{
// check if there already is a matching device.
var devRef = context.KnownDevices.FirstOrDefault(selector);
if (devRef is not null)
{
return devRef;
}
// if not, register event handler
// and wait for a matching device to show up
var eventSync = new object();
var waitForDeviceBlocker = new TaskCompletionSource();
IDeviceReference? foundDevice = null;
void ProcessReport(DeviceStateReport report)
{
lock (eventSync!)
{
if (foundDevice != null)
{
// ignore events if we've already found a device.
return;
}
if (report.Connected && selector(report.DeviceReference))
{
foundDevice = report.DeviceReference;
waitForDeviceBlocker!.TrySetResult(0);
}
}
}
IDisposable subscription = null!;
CancellationTokenRegistration registration = default;
try
{
registration = cancellationToken.Register(() => waitForDeviceBlocker.TrySetCanceled());
var deviceReportObserver = new DeviceStateObserver(ProcessReport);
subscription = context.DeviceStateReports.Subscribe(deviceReportObserver);
await waitForDeviceBlocker.Task;
}
finally
{
#if NET8_0_OR_GREATER
await registration.DisposeAsync();
#else
registration.Dispose();
#endif
subscription.Dispose();
}
#pragma warning disable S2583 // Conditionally executed code should be reachable
if (foundDevice is null)
#pragma warning restore S2583
{
// We don't document that exception because it shouldn't happen,
// and we don't expect the consumer to handle this exception type.
#pragma warning disable RCS1140 // Add exception to documentation comment.
throw new InvalidOperationException("Couldn't locate device with matching criteria. This is a bug, please file an issue.");
#pragma warning restore RCS1140
}
return foundDevice;
}
}
}