Jak na SAS klíč pro Event Hub REST API
Dnes se podíváme na první ze série článku o IoT. V prvním díle si ukážeme typické použití Azure jako integrační platformy pro IoT a to konkrétně pro sběr a následné zpracování senzorových dat.
Celý expediment, který budu popisovat v sérii článků řeší problém s řízením vytápění v rodinném domě. V mém konkrétním případě jsem potřeboval sbírat data z několika teplotních čidel a zareagovat v případě, že se teplota skokově změnila, dostala se mimo stanovený rozsah, nebo se změna projevila jen na některých čidlech. Cílem bylo monitorovat teplotu v domě a následně regulovat jednotlivé pokoje i v případě například otevřených oken a pod. Jelikož řešení musí běžet 24/7, rozhodl jsem se, kvůli spotřebě, realizovat jednotlivá čidla pomocí Arduina a hodnoty přenášet po I2C sběrnici. Data jsou pak následně agregovaná pomocí Rasppery Pi a z něj posílána do Azure Event Hubu a z něj následně zpracovaná pomocí služby Stream Analytics.
Shrnutí:
- Měření dat - Arduino
- Sběr dat - Raspberry Pi pomocí i2C
- Uložení dat - Azure Event Hub
- Zpracování dat - Azure Stream Analytics
První problém nastal, když jsem chtěl data o teplotě odesílat do azure. První verze programu vypadala následovně:
MessagingFactory factory = MessagingFactory.CreateFromConnectionString(ConnectionString);
EventHubClient client = factory.CreateEventHubClient("arduino");
string serializedString = JsonConvert.SerializeObject(temperature);
MemoryStream stream = new MemoryStream();
byte[] buffer;
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(serializedString);
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
buffer = stream.ToArray();
}
EventData eventData = new EventData(buffer);
eventData.Properties.Add("Type", "Temperature");
client.Send(eventData);
Při testování vše fungovalo jak mělo, ale při přenosu na Raspberry Pi byla výsledkem chyba
"Missing method System.Net.NetworkCredential::.ctor(string,SecureString,string) in assembly /usr/lib/mono/gac/System/4.0.0.0__b77a5c561934e089/System.dll, referenced in assembly /home/pospa/dev/Microsoft.ServiceBus.dll"
Mono dokáže hodně, ale někde člověk prostě narazí. Nu což, alternativní variantou je použít REST API. Teorie zde:
https://msdn.microsoft.com/en-us/library/azure/dn790674.aspx
Zdánlivě jednoduchý úkol na pár řádek kódu... No a o několik hodin a verzí později jsem to měl. Bohužel, nebo spíš bohudík, je REST API nekompromisní a parametry je nutné dodržet do sebemenšího detailu. Výsledný funkční kód je:
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(GetServiceUri(eventHubName, publisher));
request.Method = "POST";
request.Headers.Add("Authorization", GetSAS(eventHubName, publisher, keyName, keyValue));
request.ContentType = "application/atom+xml;type=entry;charset=utf-8";
request.Headers.Add("Type", "Temperature");
string serializedString = JsonConvert.SerializeObject(temperature);
using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(serializedString);
}
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
Console.WriteLine("> Http Response: {0}", response.StatusCode);
}
Jednotlivé metody pak:
private static Uri GetServiceUri(string eventHub, string publisher)
{
Uri uri = ServiceBusEnvironment.CreateServiceUri("https", SbNamespace,
String.Format("{0}/publishers/{1}/messages", eventHub, publisher));
return uri;
}
private static Uri GetMethodUri()
{
Uri uri = ServiceBusEnvironment.CreateServiceUri("https", SbNamespace, string.Empty);
return uri;
}
private static readonly object lockObject = new object();
private static readonly Dictionary<string, string> _sas = new Dictionary<string, string>();
private static string GetSAS(string eventHubName, string publisher, string keyName, string keyValue)
{
string key = string.Concat(eventHubName, '/', publisher, '/', keyName);
if (!_sas.ContainsKey(key))
{
lock (lockObject)
{
if (!_sas.ContainsKey(key))
{
_sas.Add(key, SharedAccessSignatureTokenProvider.GetPublisherSharedAccessSignature(GetMethodUri(), eventHubName, publisher, keyName, keyValue, TimeSpan.FromMinutes(Ttl)));
}
}
}
return _sas[key];
}
Doufám, že vám návod ušetří martyrium hledáni a testování. Komentáře parametrů jsou:
- eventHub - jméno Event Hubu z management portálu
- publisher - alfanumerické jméno systému odesílajícího data, zvolíte si samy
- SbNamespace - Service Bus namespace z management portálu
- keyName - jméno klíče z management portálu
- keyValue - Hodnota vygenerovaného klíče z management portálu
- Ttl - Time To Live, platnost vygenerovaného SAS tokenu v minutách
Pokud budete mít s REST API jakýkoliv problém, komentáře jsou tím správným místem pro dotaz. Případně se můžete obrátit na můj twitter, kde budou i nadále odkazy na veškeré mé technické články. Děkuji
-<{Pospa}>-