I should implement a method that can return a list of devices. The goal is to return a device as soon as one is found without waiting for the search to finish.
To do this I think I use IAsyncEnumerable
.
The Discover method of my implementation has an Action
that takes care of adding an item to the list as soon as one is found.
How could I modify the Action
and make sure that the new device is not added to a list but returned via "yield return"?
Below is the code I used:
public async IAsyncEnumerable<Device> ScanAsync(
CancellationToken cancellationToken = default(CancellationToken))
{
var devices = new List<DiscoveryDevice>();
await Discover(TimeoutSeconds, d => devices.Add(d), cancellationToken);
yield return new Device("", ""); //ERROR: return always last
}
private async Task Discover(int timeout, Action<DiscoveryDevice> onDeviceDiscovered,
CancellationToken cancellationToken = default(CancellationToken))
{
IEnumerable<IOnvifUdpClient> foreachInterface = clientFactory
.CreateClientForeachInterface();
if (!foreachInterface.Any())
throw new Exception(
"Missing valid NetworkInterfaces, UdpClients could not be created");
await Task.WhenAll(foreachInterface.Select(
client => Discover(timeout, client, onDeviceDiscovered,
cancellationToken)).ToArray());
}
private async Task Discover(int timeout, IOnvifUdpClient client,
Action<DiscoveryDevice> onDeviceDiscovered,
CancellationToken cancellationToken = default(CancellationToken))
{
var messageId = Guid.NewGuid();
var responses = new List<UdpReceiveResult>();
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeout));
try
{
await SendProbe(client, messageId);
while (true)
{
if (cts.IsCancellationRequested ||
cancellationToken.IsCancellationRequested)
break;
try
{
var response = await client.ReceiveAsync()
.WithCancellation(cancellationToken)
.WithCancellation(cts.Token);
if (IsAlreadyDiscovered(response, responses)) continue;
responses.Add(response);
var discoveredDevice = ProcessResponse(response, messageId);
if (discoveredDevice != null)
{
Task.Run(() => onDeviceDiscovered(discoveredDevice));
}
}
catch (Exception)
{ }
}
}
finally
{
client.Close();
}
}