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}>-