4

Despite my efforts, I did not find any tutorials on how to get live sensor data via Azure IoT Hub to a web client using SignalR, where the web backend is running on ASP.net Core. I somehow get it running, so here's what I did, and a couple questions.

First, using Azure Iot Hub is really great since I'm using the device management and securing the devices and connections are easy. The data will go into Stream Analytics and Azure Functions for further processing, but live data is also available on an Event Hub.

On the client side, we are using a Vue.js setup, connecting to an API running ASP.net Core 2.1. SignalR uses the Azure SignalR Service. This is described in the docs and my setup code looks something like:

app.UseFileServer();
app.UseAzureSignalR(routes =>
{
    routes.MapHub<NotificationHub>("/notification");
});

Then, in my hub, I can use the groups for handling subscriptions as multiple clients can subscribe to the same device:

public class NotificationHub : Hub
{
    public async Task Subscribe(string deviceId)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, deviceId);
    }
    public async Task UnSubscribe(string deviceId)
    {
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, deviceId);
    }
}

The hub context is available for injection, so a pseudo code for a controller wanting to use SignalR would be:

public class MyController : Controller
{
    private readonly IHubContext<NotificationHub> _hubContext;

    public MyController(IHubContext<NotificationHub> hubContext)
    {
        _hubContext = hubContext;
    }

    public async Task<IActionResult> MyMethod()
    {
        ...
        await _hubContext.Clients.Group("a group").SendAsync("TheMessage", "The data");
        ...
    }
}

So far, everything seemed straight forward.

Then I wanted to implement a listener for the Event Hub, and running this as a background task. Turns out that we only need one line of code for registering a hosted service in our Startup.cs:

services.AddHostedService<IoTEventHubService>();

The IoTEventHubService would inherit from BackgroundService, which is found in Microsoft.Extensions.Hosting:

public class IoTEventHubService : BackgroundService
{
    private readonly IHubContext<NotificationHub> _hubContext;

    public IoTEventHubService(IHubContext<NotificationHub> hubContext)
    {
        _hubContext = hubContext;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        ...defining the names and connection strings...

        var eventProcessorHost = new EventProcessorHost(
            eventProcessorHostName,
            _eventHubName,
            PartitionReceiver.DefaultConsumerGroupName,
            _eventHubConnectionString,
            _storageConnectionString,
            leaseName);

        var options = new EventProcessorOptions
        {
            InitialOffsetProvider = (partitionId) => 
                EventPosition.FromEnqueuedTime(DateTime.UtcNow)
        };

        // Registers the Event Processor Host and starts receiving messages
        await eventProcessorHost.RegisterEventProcessorFactoryAsync(
            new IoTEventProcessorFactory(_hubContext), options);
    }

In order to pass a parameter, _hubContext, I had to use the factory method to register my event processor. My factory, IoTEventProcessorFactory, inherits from IEventProcessorFactory and pass the hub context further to an event processor in the CreateEventProcessor method,

public IEventProcessor CreateEventProcessor(PartitionContext context)
{
    return new IoTEventProcessor(_hubContext);
}

The IoTEventProcessor class inherits from IEventProcessor, and process the messages like the following:

public Task ProcessEventsAsync(PartitionContext context, IEnumerable<EventData> messages)
{
    foreach (var eventData in messages)
    {
        // De-serializing into a IoTMessage class
        var data = JsonConvert.DeserializeObject<IoTMessage>(
            Encoding.UTF8.GetString(eventData.Body.Array));

        // I can get the device id as defined in the IoT Hub
        data.DeviceId = eventData.SystemProperties.GetValueOrDefault(
            "iothub-connection-device-id").ToString();

        // And also the time for which the message was processed
        data.EventEnqueuedUtcTime = DateTime.Parse(
            eventData.SystemProperties.GetValueOrDefault("iothub-enqueuedtime").ToString());

      // Using the hub context, we can send live data to subscribers
      _hubContext.Clients.Group(data.DeviceId).SendAsync("LiveDataMessage", data);
    }

    return context.CheckpointAsync();
}

This works and the client is receiving live data, but is it correctly implemented? Should I dispose the event processor host somewhere? In my Application Insights, I see a large number (>1000/hour, when no sensor data is coming in) of dependencies "failures" for the IoT Hub connection, but the status is False. Is this just saying that it wasn't any sensor data..?

Pal-B
  • 139
  • 1
  • 7

0 Answers0