Извлечь IoT Hub Twin из функции Azure, вызванной ServiceBus - PullRequest
0 голосов
/ 08 октября 2018

Мы отправляем данные с устройств IoT в концентратор IoT Azure и пытаемся передать сообщение определенного типа в функцию Azure.

На данный момент мы сделали это, создав конечную точку Azure Service Bus и создав маршрутизация сообщений в IoTHub.Он работает, как и ожидалось, с корректным получением сообщения функцией Azure.

Теперь мы хотели бы получить DeviceId из IoT-хаба в функции Azure, а также Теги определены в Device Twin , и я совершенно потерян в том, как это сделать.

Если бы мы использовали EventHubTrigger, кажется, что этобыло бы просто сделать что-то вроде этого:

public static class Test
{
    [FunctionName("TestQueueTrigger")]
    public static void Run(
        [EventHubTrigger("messages/events", Connection = "IoTHubConnection")]
        EventData message,
        Twin deviceTwin,
        TraceWriter log)
    { ... }
}

Но на самом деле не совсем ясно, как это можно сделать с помощью триггера служебной шины.

Кроме того, мы хотели бы сохранить все сообщения (независимо от маршрута) в хранилище озера данных Azure, и я немного растерялся из-за того, как это будет работать.

1 Ответ

0 голосов
/ 09 октября 2018

Формат сообщения устройства-облака Azure IoT Hub описан здесь .В этом формате нет двойных свойств устройства.Близнецы устройства хранятся в облачном бэкэнде, об их изменениях можно уведомлять на основе маршрутов iot-концентратора к конкретной конечной точке (встроенной и / или настраиваемой конечной точке).

Ваш пример функции " TestQueueTrigger"использует azure-functions-iothub-extension для версии 1. Входная привязка расширения Twin позволяет получить двойник устройства с помощью отдельного вызова внутри расширения:

deviceTwin = await registryManager.GetTwinAsync(attribute.DeviceId);

По сути, это расширение может использоваться также для привязки ServiceBusTrigger .Обратите внимание, что это расширение может использоваться только для функции версии 1, поэтому я рекомендую получить двойник устройства внутри функции, используя, например, REST API Get Twin call.

Обновление

В следующем фрагменте кода показан пример функции ServiceBusTrigger и вызова REST API Get Twin.

run.csx file:

#r "..\\bin\\Microsoft.Azure.ServiceBus.dll"
#r "..\\bin\\Microsoft.Azure.Devices.Shared.dll"
#r "Microsoft.Azure.WebJobs.ServiceBus"
#r "Newtonsoft.Json"


using System;
using System.Threading.Tasks;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Microsoft.Azure.WebJobs.ServiceBus;
using Microsoft.Azure.ServiceBus;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Web;
using Microsoft.Azure.Devices.Shared;

// reusable proxy
static HttpClientHelper iothub = new HttpClientHelper(Environment.GetEnvironmentVariable("AzureIoTHubShariedAccessPolicy"));

public static async Task Run(Message queueItem, ILogger log)
{
    // payload
    log.LogInformation($"C# ServiceBus queue trigger function processed message: {Encoding.UTF8.GetString(queueItem.Body)}");

    // device identity Id
    var deviceId = queueItem.UserProperties["iothub-connection-device-id"];

    // get the device twin
    var response = await iothub.Client.GetAsync($"/twins/{deviceId}?api-version=2018-06-30");
    response.EnsureSuccessStatusCode();
    Twin twin = await response.Content.ReadAsAsync<Twin>();

    log.LogInformation(JsonConvert.SerializeObject(twin.Tags, Formatting.Indented));

    await Task.CompletedTask;
}


// helpers
class HttpClientHelper
{
    HttpClient client;
    DateTime expiringSaS;
    (string hostname, string keyname, string key) config;

    public HttpClientHelper(string connectionString)
    {
        config = GetPartsFromConnectionString(connectionString);
        client = new HttpClient() { BaseAddress = new Uri($"https://{config.hostname}")};
        SetAuthorizationHeader();         
    }

    public HttpClient Client
    {
        get
        {          
            if (expiringSaS < DateTime.UtcNow.AddMinutes(-1))
            {
               SetAuthorizationHeader();  
            }         
            return client;
        }
    }

    internal void SetAuthorizationHeader()
    {
        lock (client)
        {
            if (expiringSaS < DateTime.UtcNow.AddMinutes(-1)) 
            {
                string sasToken = GetSASToken(config.hostname, config.key, config.keyname, 1);
                if (client.DefaultRequestHeaders.Contains("Authorization"))
                    client.DefaultRequestHeaders.Remove("Authorization");
                client.DefaultRequestHeaders.Add("Authorization", sasToken);
                expiringSaS = DateTime.UtcNow.AddHours(1);
            }
        }
    }

    internal (string hostname, string keyname, string key) GetPartsFromConnectionString(string connectionString)
    {
        var parts = connectionString.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Split(new[] { '=' }, 2)).ToDictionary(x => x[0].Trim(), x => x[1].Trim());
        return (parts["HostName"] ?? "", parts["SharedAccessKeyName"] ?? "", parts["SharedAccessKey"] ?? "");
    }

    internal string GetSASToken(string resourceUri, string key, string keyName = null, uint hours = 24)
    {
        var expiry = GetExpiry(hours);
        string stringToSign = HttpUtility.UrlEncode(resourceUri) + "\n" + expiry;
        HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(key));

        var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
        var sasToken = String.Format(CultureInfo.InvariantCulture, $"SharedAccessSignature sr={HttpUtility.UrlEncode(resourceUri)}&sig={HttpUtility.UrlEncode(signature)}&se={expiry}");
        if (!string.IsNullOrEmpty(keyName))
            sasToken += $"&skn={keyName}";
        return sasToken;
    }

    internal string GetExpiry(uint hours = 24)
    {
        TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
        return Convert.ToString((int)sinceEpoch.TotalSeconds + 3600 * hours);
    }
}

function.json:

{
  "bindings": [
    {
      "name": "queueItem",
      "type": "serviceBusTrigger",
      "direction": "in",
      "queueName": "myQueue",
      "connection": "myConnectionString_SERVICEBUS"
    }
  ]
}
...