Gadgeteer und Azure

Hardware meets Software in der Cloud
Kommentare

Dass wir von unterwegs aus unsere Heizung zuhause steuern können, wissen wir alle – zeigt uns das die Werbung doch täglich. Wie das aber genau geht, wissen auch viele gestandene Softwareentwickler nicht. Und auch die Hardwarespezialisten wissen oftmals nicht mehr; daher benötigt die Entwicklung solcher Lösungen derzeit noch Spezialisten aus den unterschiedlichsten Bereichen. Doch muss das so sein? Tatsächlich gibt es zwischenzeitlich gute Ansätze und Werkzeuge, die hier Brücken schlagen und Möglichkeiten bieten.

Vor wenigen Jahren traf die Aussage noch zu, dass Hardware- und Softwareentwicklung jeweils für sich alleine betrachtet so komplexe Aufgaben darstellen, dass beides gemeinsam unmöglich von einer Person alleine bewältigt werden kann. Doch auf diesem Gebiet tut sich etwas, es existieren Werkzeuge, die die Welten sehr eng zusammenbringen. Auch Microsoft hat hier eine sehr imposante, da durchgängige Story zu bieten. Mit Visual Studio kann in einer Entwicklungsumgebung, ja in sogar einer einzigen Solution alles zusammengefasst werden, was für die Anbindung eines Embedded-Systems an die Cloud benötigt wird. Es stehen Programmiersprachen und -werkzeuge zur Verfügung, die für die Cliententwicklung und für die Implementierung hardwarenaher Funktionen verwendet werden können.

In diesem Artikel wird anhand eines kleinen Showcases die Nutzung von Visual Studio gezeigt. Dabei kommen folgende Technologien zum Einsatz:

  • Hardware: Gadgeteer Evaluation System
  • Cloud: Azure Mobile Services
  • Client: Windows RT 8.1/Windows Phone 8.1

Gadgeteer und .NET Micro Framework

Hinter Gadgeteer steht eine Open-Source-Initiative, die die Entwicklung von Embedded-Systemen auf Basis des Microsoft .NET Micro Framework (kurz NETMF) vereinfachen will. Dazu sollen die Software und die Hardware frei verfügbar sein. Daher ist das Microsoft .NET Mirco Framework selbst ein Open-Source-Projekt, das von Microsoft initiiert auf Codeplex gehostet wird. Das Besondere: Die Hardware folgt generell dem gleichen Prinzip, siehe hier. Auf dieser Basis liefern unterschiedliche Hardwarehersteller Bauteile, die über standardisierte Stecker miteinander verbunden, aufgesteckt oder direkt verdrahtet werden können. Zu den Komponenten kommt der Treiber dann quasi als NETMF Assembly direkt mit. Die meisten Hersteller bieten ein Basisset, das zur Evaluierung herangezogen und mit weiteren Komponenten erweitert werden kann. Das trägt zu einer deutlichen Vereinfachung des Prototyping im Hardwarebereich bei. Auf diesem Hardwareprototyp wird entwickelt. Hierzu wird das System in der Regel per USB mit dem Entwickler-PC verbunden, NETMF bringt alles zum Entwickeln und Debuggen unter Visual Studio mit. Die Entwicklung selbst erfolgt in gewohnter .NET-Manier. Anders ist nur das .NET Micro Framework selbst. Dieses beschränkt sich auf die wesentlichen Dinge, die für die jeweilige Hardware sinnvoll sind. So werden z. B. IO-Ports der Hardware als .NET-Objekte zur Verfügung gestellt, die Signaländerung wird über ein .NET-Event an die Anwendung übertragen.

Azure Mobile Services

Die nächste Aufgabe ist die Anbindung der Hardware an den Rest der Welt. Hier können unterschiedliche Ansätze gewählt werden. In der Vergangenheit wurde im Anlagenbau z. B. sehr häufig ein kompletter PC neben das Embedded-System gestellt und über RS232/485, CAN, OPC oder ein anderes Feldbussystem mit der Hardware verbunden, da die Entwicklungskosten für eine direkte Anbindung der Hardware in keinem Verhältnis zu den Kosten eines weiteren PCs standen. Im Zeitalter des Internet of Things ist ein solches Modell nicht mehr tragbar, sollen doch die angeschlossen Geräte in großer Stückzahl zu einem Preis unter 100 Euro angeboten werden können.

Daher müssen auch hier Mechanismen gesucht werden, die den Aufbau vereinfachen und vor allem direkt in die eigentliche Hardware integriert werden können. Klassisch werden Web Services, SOAP oder WCF verwendet. Diese Protokolle sind jedoch für kleinere Geräte mit wenig Speicher eher ungeeignet, da die Daten entweder in XML verpackt werden oder bei einer binären Übertragung von der Plattform abhängig sind. REST bietet hier deutliche Vorteile, da viele Informationen bereits über den URL und den HTTP-Header übertragen werden und die Nutzdaten nicht mehr aufwendig in XML verpackt, sondern mit deutlich weniger Protokoll-Overhead direkt als String übertragen werden.

Microsoft stellt mit Azure Mobile Services eine interessante REST-Plattform zur Verfügung, die in der Cloud und auch On-Premise gehostet werden kann. Um ins Internet zu gelangen, muss an der Implementierung der Anwendung nichts geändert werden. Das ist nur eine Frage des Hostings, folglich hat man hier auch eine sehr gute Skalierung. Die Entwicklung eines Azure Mobile Service läuft dabei komplett in Visual Studio. Der Service wird während der Entwicklung lokal in IIS Express oder auf einem lokalen IIS gehostet und kann dann per Knopfdruck über Web Deploy auf einen On-Premise IIS oder auf einen Azure-Server im Internet ausgerollt werden. Hierfür hat Microsoft auch das Thema Schemaupgrade von Datenbanken in Produktion mit adressiert, ebenfalls wurde die Authentisierung entsprechend überdacht. So genügt bei einer eigentlich anonymen Anwendung die Anmeldung über einen Application Key, der von Azure für jeden Service eindeutig generiert und dann vom Entwickler fest in die Clientanwendungen integriert wird. Für wirklich personalisierte Anwendungen bieten Azure Mobile Services Unterstützung über diverse Token-Provider an: Microsoft-Account, Facebook, Twitter, Google und Windows Azure Active Directory.

Mobile-Clients – Windows und Windows Phone

Interessant an Windows 8.1 und Windows Phone 8.1 ist die sehr gute und einfache Art der Anbindung an das Azure-Mobile-Services-Modell. Daneben ist aber auch zu erwähnen, dass natürlich auch andere Mobile-Devices mit Azure Mobile Services kommunizieren können. REST basiert auf reinen textbasierten HTTP-Anforderungen. So kann die Schnittstelle des Azure Mobile Service auch von einer Android- oder iOS-Anwendung angesprochen werden. Xamarin bietet hier einen Ansatz an, die Entwicklung der Benutzerschnittstelle zu vereinfachen (mehr hierzu war bereits in der Windows-Developer-Ausgabe 1/2015 zu lesen). Für Windows und Windows Phone bietet Microsoft Universal-Apps an, die für unsere Zwecke Verwendung finden.

Unser Showcase

Der Showcase führt die beschriebenen Technologien zusammen (Abb. 1). Wer möchte, kann den Showcase schrittweise nachvollziehen und erhält damit eine erste Vorlage für eigene Applikationen.

 Abb. 1: Übersicht Testaufbau

Auf der Hardwareseite besteht die Anwendung im Wesentlichen aus einem Schalter und einer LED (Abb. 2). Beide sind an das FEZ Spider Mainboard angeschlossen, das über ein LAN-Modul mit dem Azure Mobile Service kommuniziert. Das USB-Modul wird für die Anbindung an Visual Studio und zur Spannungsversorgung genutzt.

 Abb.2: FEZ-Spider-Testaufbau

Wird der Schalter betätigt, so schaltet die LED zyklisch auf die nächste Farbe um, von Rot auf Grün auf Blau und wieder auf Rot. Zusätzlich wird jeder Farbwechsel an den Azure Mobile Service gesendet. Dort wird die Farbe mit einem Zeitstempel versehen in einer SQL-Datenbank abgelegt (Abb. 3). Die protokollierten Einträge können danach von einem Mobile-Client abgerufen werden, dieser kann alternativ auch selbst Einträge schreiben. Der Rückkanal zur Hardware ist dann im Grunde nur die Umkehr des Vorgehens.

 Abb. 3: Ablauf in der Anwendung

Alle benötigten Softwareprojekte werden gemeinsam in einer Solution in Visual Studio bearbeitet (Abb. 4).

 Abb. 4: Visual Studio Solution

Die Projekte in der Übersicht:

  • artiso_iotGadgeteer: Implementierung für die Hardware
  • artiso_iotService: Azure Mobile Service
  • artiso_iot: Universal-App für die Clients

Aufmacherbild: Cloud Computing Retro symbolConcept von Shutterstock.com / Urheberrecht: Gunnar Assmy [ header = Seite 2: Hardwarenahe Programmierung für den Gadgeteer]

Hardwarenahe Programmierung für den Gadgeteer

Für die Programmierung des Gadgeteer wird zunächst eine entsprechende Entwicklungsumgebung benötigt. Für den Showcase wurde herangezogen:

Vorgenommen wurde die Installation auf einem Windows 2012 R2 Server Standard; Windows 7/8/8.1 sind hier aber ebenfalls möglich. Wichtig ist jedoch, dass das Image nicht als VM ausgeführt wird, der von GHI gelieferte USB-Treiber unterstützt bislang noch nicht den mit Windows 8.0 und Windows 2012 eingeführten VM-Bus und kann somit nur auf einem physikalischen System genutzt werden. Mehr zur Konfiguration kann der Installationsanleitung von GHI entnommen werden.

Nach erfolgter Installation steht in Visual Studio ein neuer Projekttyp für Gadgeteer-Projekte zur Verfügung (Abb. 5).

 Abb. 5: Visual Studio Gadgeteer Wizard

In dem über den Wizard angelegten Projekt steht ein grafischer Editor zur Verfügung, mit dem zunächst die verwendeten Hardwarekomponenten konfiguriert werden. Hierzu werden einfach über die Toolbox die Komponenten per Drag and Drop auf den Gadgeteer-Designer gezogen (Abb. 6).

 Abb. 6: Gadgeteer-Designer

Das Gadgeteer Mainboard stellt unterschiedliche Verbinder zur Verfügung. Diese sind durchnummeriert und mit Buchstaben für die Art der Beschaltung und des Protokolls gekennzeichnet. Der Gadgeteer-Designer enthält die Logik zur Prüfung, welche Komponente an welchem Socket angeschlossen werden kann; eine Falschbelegung im Designer ist somit nicht möglich.

Beim physikalischen Aufbau ist dann etwas mehr Aufmerksamkeit geboten, da die Steckkontakte selbst identisch sind. Aber einfach den Zahlen im Designer folgen, führt auch hier sicher zum Ziel. Die unterschiedlichen Sockets des GHI FEZ Spider Kits und die im Kit enthaltenen Module sind hier beschrieben.

Nun kann mit der Implementierung der Software begonnen werden. In der vom Wizard generierten Datei Program.cs ist die Funktion ProgramStarted zu finden. Diese wird bei der Initialisierung der Anwendung als Erstes durchlaufen und sollte diese innerhalb von 10 Sekunden abgeschlossen haben. Reicht diese Zeit nicht aus, kann ein Timer genutzt werden, um länger laufende Initialisierungen leicht zeitverzögert auszuführen (Listing 1).

void ProgramStarted()
{
  // Use Debug.Print to show messages 
  // in Visual Studio's "Output" window 
  // during debugging.
  Debug.Print("Program Started");

  button.ButtonPressed += button_ButtonPressed;
  led.TurnColor(GT.Color.Orange);
}

void button_ButtonPressed(
  Button sender, Button.ButtonState state)
{
  Color c = led.GetCurrentColor();
  if (c == GT.Color.Black) led.TurnRed();
  else if (c == GT.Color.Red) led.TurnGreen();
  else if (c == GT.Color.Green) led.TurnBlue();
  else led.TurnOff();
}

Nach dem Übersetzen der Quellcodes und Herunterladen auf das Board kann die Anwendung direkt auf der Hardware gestartet und auch debuggt werden. Wie in Visual Studio üblich, einfach durch F5 drücken, und der Rest wird von der IDE erledigt. Die feinen Unterschiede werden dann während des Debuggens klar. So laufen z. B. auf der Hardware Timeouts auf, auch werden Überwachungen von Variablen nicht umgehend und in jedem Kontext aktualisiert. Aber es dauert erfreulich lange, bis dem Entwickler dieses Verhalten überhaupt auffällt.

Durch den Download wird das Programm auf dem Embedded-System automatisch persistiert. Das Evaluierungsboard kann nun vom USB-Port des PCs getrennt werden. Zur Spannungsversorgung kann es stattdessen an ein herkömmliches USB-Ladegerät oder ein separates Steckernetzteil angeschlossen werden und spult dann unaufhörlich das installierte Programm ab.

Gadgeteer und LAN

Um das Gadgeteer-System ans Internet zu bringen, muss ein Baustein für LAN oder WLAN angeschlossen werden. Die Konfiguration der LAN-Komponenten muss entweder im Programm selbst oder über die USB-Schnittstelle und eine von GHI gelieferte Konfigurationssoftware vorgenommen werden. Empfehlenswert ist Letzteres, da hiermit auch später sehr einfach IP-Adresse, Gateway und DNS-Server an die Gegebenheiten in der Einsatzumgebung angepasst werden können.

Anschließend muss innerhalb der Anwendung die Initialisierung der Netzwerkkarte angestoßen werden, die Konfigurationswerte selbst müssen nicht mehr explizit angegeben werden. Etwas trickreich dabei ist der Umgang mit DHCP, da das Anfordern einer Lease etwas Zeit in Anspruch nimmt und die IP-Adresse daher erst etwas später zur Verfügung steht (Listing 2).

void ProgramStarted()
{
  // ...
 
  ethernet.Interface.CableConnectivityChanged +=
    Interface_CableConnectivityChanged;
  ethernet.Interface.NetworkAddressChanged +=
    Interface_NetworkAddressChanged;
  ethernet.UseThisNetworkInterface();
  
  if (ethernet.Interface.IsCableConnected)
  {
    if (ethernet.NetworkSettings.IsDhcpEnabled)
      ethernet.NetworkSettings.EnableDhcp();
    else
      NetworkAvailable();
  }
}

void Interface_NetworkAddressChanged(
  object sender, EventArgs e)
{
  NetworkAvailable();
}

void NetworkAvailable()
{
  System.Net.IPAddress ip =
    System.Net.IPAddress.GetDefaultLocalAddress();

  if (ip != System.Net.IPAddress.Any)
  {
    // setup the device time
    DateTime networkTime =
      MFToolkit.Net.Ntp.NtpClient.GetNetworkTime();
    Microsoft.SPOT.Hardware.Utility.SetLocalTime(
      networkTime);

    // get settings from azure
    AzureServiceGet();
  }
}

Ferner ist eine aktuelle Uhrzeit im Embedded-Bereich nicht selbstredend. So kommt das hier verwendete FEZ-Spider-System komplett ohne Realtime-Clock, und die Uhrzeit muss von einem NTP-Server aus dem Netz bezogen werden. Hier hilft das erste Mal der Open-Source-Gedanke, eine entsprechende Implantierung kann schnell mit dem MFToolkit auf Codeplex gefunden werden.

[ header = Seite 3: Programmierung Azure Mobile Service]

Programmierung Azure Mobile Service

Mit Azure Mobile Services stellt Microsoft einen Dienst zur zentralen Bereitstellung von Funktionalität für mobile Anwendungen zur Verfügung. Die fünf von Azure Mobile Service angebotenen Kernfunktionalitäten sind Datenbanken, Web-API über REST, Authentisierung, Push Notifications und die Planung von wiederkehrenden Aufgaben.

Das grundlegende Konzept der Azure Mobile Services wurde bereits in Ausgabe 1.2015 des Windows Developers erläutert. Die Grundlegende Programmierung kann ausführlich auch direkt auf der Azure-Plattform nachgelesen werden.

Azure Mobile Services wurden zwar speziell für den Betrieb von serverseitigen Komponenten für verteilte mobile Anwendungen konzipiert, stehen aber auch jeder anderen Anwendung zur Verfügung. Der Charme an der Kombination von Embedded-Systemen im Home-Automation-Bereich mit der Cloud ist offensichtlich, lassen sich damit doch Szenarien wie eben die Versteuerung der Heizung in den eigenen vier Wänden von unterwegs aus realisieren. Wird der mobile Aspekt nicht benötigt oder soll das Smartphone bzw. Tablet nur zuhause oder im eignen Unternehmen genutzt werden, so kann komplett auf die Cloud verzichtet werden, indem der Mobile Service lokal auf einem IIS gehostet wird.

Das Einrichten eines Azure Mobile Service ist denkbar einfach. Benötigt wird lediglich ein Microsoft-Benutzerkonto und schon kann es nach einer Registrierung über das Microsoft-Azure-Portal losgehen. Beim Anlegen eines Azure Mobile Service erhält der Nutzer zum einen ein Visual-Studio-2013-Codegerüst, das er herunterladen, übersetzen und direkt testen kann, zum anderen werden auch die für eine Absicherung des Diensts vor unberechtigtem Zugriff benötigten Keys zur Verfügung gestellt. Das Codegerüst enthält bereits einen kleinen Beispieldienst artiso_iotService und eine Universal-App für Windows-Modern-UI und Windows-Phone-Ordner artiso_iot und kann einfach in die für das Gadgeteer-Projekt erstellte Visual Studio Solution integriert werden.

 Abb. 7: Das Projekt „artiso-iotService“

In Abbildung 7 sind die beiden wesentlichen Aspekte unseres Azure Mobile Service in den beiden Ordnern DataObjects und Controllers dargestellt. Das generierte Beispiel enthält unter DataObjects die Datei TodoItem.cs; für den Showcase wird analog hierzu die Datei ColorChange.cs ergänzt, die lediglich die Definition einer Klasse enthält:

public class ColorChange : EntityData
{
  public DateTime TimeStamp { get; set; }
    
  public int Color { get; set; }
}

Diese Klasse wird von den Azure Mobile Services über Entity Framework als Schema für eine entsprechende SQL-Server-Datenbank herangezogen, die beim Ausrollen auf Azure automatisch erstellt und später die an den Service übermittelten Daten enthalten wird.

Der Ordner Controllers enthält die generierte Datei TodoItemController.cs, welche wiederum analog um die Datei ColorChangeController.cs ergänzt wird. Diese enthält die Methoden, die bei einem Zugriff per REST-API durch einen Client angesprochen werden. Hier kann Logik seitens des Service implementiert werden, wenngleich einfache Services mit dem hier gezeigten Standard meist auskommen können (Listing 3).

public class ColorChangeController :
  TableController
{
  protected override void  Initialize(
    HttpControllerContext controllerContext)
  {
    base.Initialize(controllerContext);
    artiso_iotContext context =
      new artiso_iotContext();
    DomainManager =
      new EntityDomainManager(
        context, Request, Services);
  }

  // GET tables/ColorChange
  public IQueryable GetAllColorChanges()
  {
    return Query();
  }

  // GET tables/ColorChange/
  public SingleResult GetColorChange(
    string id)
  {
    return Lookup(id);
  }

  // PATCH tables/ColorChange/
  public Task PatchColorChange(
    string id, Delta patch)
  {
    return UpdateAsync(id, patch);
  }

  // POST tables/ColorChange
  public async TaskPostColorChange(
    ColorChange item)
  {
    // overwirte the TimeStamp for new items
    item.TimeStamp = System.DateTime.Now;

    ColorChange current = await InsertAsync(item);
    return CreatedAtRoute(
      "Tables", new { id = current.Id }, current);
  }

  // DELETE tables/ColorChange/
  public Task DeleteColorChange(string id)
  {
    return DeleteAsync(id);
  }
}

Den Kommentaren im Code ist zu entnehmen, über welchen URL und welche Methode ein Client die entsprechende Funktion auslösen kann. Zusammengeführt werden alle durch den Service unterstützten Entitäten durch die Datei artiso_iotContext.cs im Ordner Models (Listing 4).

public class artiso_iotContext : DbContext
{
  // ...

  private const string connectionStringName =
    "Name=MS_TableConnectionString";
  public artiso_iotContext() :
    base(connectionStringName) { } 

  public DbSet TodoItems { get; set; }
  public DbSet ColorChanges { get; set; }

  // ...
}

Dort werden die Entitäten einfach als DbSet ausgewiesen.

Ansprechen des Azure Mobile Service über Browser

Abbildung 8 zeigt auch bereits, wie ein Azure Mobile Service auf einem lokal installierten IIS ausgeführt werden kann. In den Projekteinstellungen wird hierzu unter Web | Servers eine der Optionen „IIS Express“, „Local IIS“ oder „External Host“ ausgewählt. Danach kann das Projekt als Startup Project gewählt und der Service aus Visual Studio heraus über „IIS Express“ gestartet werden. Im Browser kann dann die Funktionalität über REST angesprochen werden: „try it out“/“GET tables/ColorChange“/“try this out“/“send“ führt die Methode direkt aus und liefert eine Liste der bereits erfassten Farbänderungen zurück (Abb. 9).

 Abb. 8: Azure Mobile Service auf lokalem IIS

 Abb. 9: Ausführung eines Azure Mobile Service unter IIS Express

Das Kontextmenü des Projekts erlaubt ferner das Publizieren des Azure Mobile Service in die Microsoft-Cloud. Nach Auswahl von „Microsoft Azure Mobile Services“ und Anmeldung mit dem Microsoft-Account wird unmittelbar eine Liste der auf den Benutzer registrierten Mobile Services angeboten. Die notwendigen Einstellungen für das Publizieren werden nach Auswahl des gewünschten Diensts direkt aus der Cloud abgerufen. Nach der Veröffentlichung kann der Dienst dann auch in der Cloud getestet werden. Einziger Unterschied: Es wird nun eine Anmeldung nötig, zunächst für den reinen Webzugriff auf dem Cloud-Host (der entsprechende Account wird beim Veröffentlichen angezeigt, im konkreten Fall lautet er $mobile$artiso-iot), als Passwort wird hier der Master-Key des Mobile Service genutzt. Ferner muss beim Aufruf einer Methode im Header der Anforderung die Eigenschaft X-ZUMO-APPLICATION mit dem Application Key des Mobile Service belegt werden.

Universal-App als Azure-Mobile-Client

Im Solution-Ordner [artiso_iot] finden sich drei Projekte:

  • artiso_iot.Windows (UI Win 8.1)
  • artiso_iot.WindowsPhone (UI Win Phone 8.1)
  • artiso_iot.Shared (Anbindung an den Service)

Bezüglich der Benutzerschnittstelle der mobilen Anwendungen gibt es nichts Besonderes zu berichten. Diese werden wie alle anderen Anwendungen für diese Plattformen erstellt. Die Universal-App teilt dabei recht geschickt den plattformspezifischen Code, der vornehmlich aus XAML besteht, und den portablen Code auf unterschiedliche Projekte auf. Der portable Code wird in ein separates „Shared Project“ gelegt.

Die Anbindung an den Azure Mobile Service erfolgt daher auch im Projekt artiso_iot.Shared, das jeweils von den beiden anderen Projekten referenziert wird. Dort findet sich im Ordner DataModel erneut eine Repräsentation der Entitäten des Mobile Service, die Datei TodoItem.cs wurde wieder generiert, die Datei [ColorChange.cs] entsprechend für unsere Anwendung angepasst:

public class ColorChange
{
  public string Id { get; set; }

  [JsonProperty(PropertyName = "TimeStamp")]
  public DateTime TimeStamp { get; set; }

  [JsonProperty(PropertyName = "Color")]
  public int Color { get; set; }
}

Für die Serialisierung in JSON wird dabei das NuGet Package Newtonsoft.Json genutzt, das die Serialisierung über Attribute steuert. In der Datei App.xaml.cs wird eine Instanz der Klasse MobileServiceClient angelegt, die den Zugriff auf den Mobile Service erlaubt. Der folgende Codeausschnitt zeigt den Zugriff für die drei unterschiedlichen Hostingumgebungen (Listing 5).

sealed partial class App : Application
{
  // for IIS Express 
  public static MobileServiceClient
   MobileServiceIisExpress = 
    new  MobileServiceClient(
      "ht tp://localhost:58201");
  // for IIS
  public static MobileServiceClient
    MobileServiceIisLocal = 
      new MobileServiceClient(
        "ht tp:///artiso-iotService/",
        "Overridden by portal settings");
  public static MobileServiceClient
    MobileServiceAzure = 
      new MobileServiceClient(
        "ht tp://artiso-iot.azure-mobile.net",
        "");

  // choose the mobile service to use
  public static MobileServiceClient 
    MobileService = MobileServiceAzure;

  // ...
}

Die portable Code-Behind-Implementierung der in der Mobile-App dargestellten Seite liegt in der generierten Beispielanwendung in der Datei MainPage.cs, diese wird für unseren Showcase wiederum analog in der Datei ColorChangePage.cs angepasst (Listing 6).

sealed partial class ColorChangePage: Page
{
  private MobileServiceCollection
     items;
  private IMobileServiceTable
    colorChangeTable =
      App.MobileService.GetTable();
  // ...
  private async Task RefreshColorChanges()
  {
    try
    {
      // this query filters the last 3 elements
      IMobileServiceTableQuery query =
        colorChangeTable.OrderByDescending(
          cc => cc.TimeStamp).Take(3);

      // execute the query
      items = await query.ToCollectionAsync(); 
    }
    catch (MobileServiceInvalidOperationException e)
    {
      // ...
    }

    ListItems.ItemsSource = items;
    // ...
  }
  // ...
}

Gut zu erkennen ist der Zugriff auf den Mobile Service über das Applikationsobjekt und das dort angelegte Element MobileService. Per GetTable kann eine Referenz auf eine Tabelle ermittelt und auf dieser anschließend eine LINQ-Abfrage erstellt werden. Wird diese mittels ToCollectionAsync ausgeführt, liefert der Mobile Service eine MobileServiceCollection zurück, die direkt als ItemsSource an ein Bedienelement gebunden werden kann. Die Abfrage wird dabei als URL-Parameter per REST an den Service übergeben, d. h. das Filtern läuft komplett durch den Service ab. Die zu übertragende Datenmenge wird hierdurch klein gehalten, die Abfrage selbst wird durch den dahinter liegenden SQL-Server optimiert.

[ header = Seite 4: Bring it together – Azure Mobile Services in NETMF]

Bring it together – Azure Mobile Services in NETMF

Wieder auf dem Gadgeteer angekommen, stellt man jedoch stellt, dass sich hier die Nutzung des Azure Mobile Service nicht ganz so einfach gestaltet. Die Suche nach einem Ebenbild der Klasse MobileServiceClient in NETMF fällt schnell negativ aus. Auch das Einbinden des NuGet Packages Json.NetMF ist recht ernüchternd, kann diese Implementierung seinem großen Bruder Newtonsoft.Json aus der .NET-Welt doch bei Weitem nicht das Wasser reichen. Und selbst auf der Klasse string angekommen, nimmt die Ernüchterung kein Ende, fehlen hier so grundlegende Funktionen wie string.Format. Ursache hierfür ist zum einen eventuell auch der Open-Source-Ansatz, zum anderen aber vor allem der Sachverhalt, das Garbage Collecting auf einem Embedded-System teilweise nachteilig sein kann und daher Funktionen, die viel Datenmüll produzieren, von Anfang an vermieden werden sollten.

Aber es kann auch von Hand eine Aufbereitung der entsprechenden JSON Strings vorgenommen werden. NETMF kommt mit einem guten Web-Request-/Web-Response-Modell daher, tatsächlich entsteht damit der größte Aufwand bei der Nutzung der Azure Mobile Services in der Aufbereitung der entsprechenden JSON Strings für die REST-Kommunikation. Recht hilfreich sind hier Tools wie Fiddler oder Wireshark, die das Aufzeichnen und eine Analyse von http-Paketen zulassen. Zusammen mit dem Webinterface des Mobile Service und den Windows-Mobile-Clients kann recht schnell ermittelt werden, was die Embedded-Lösung zu senden hat. Hier nun die komplette Implementierung zur Nutzung unseres ColorChange-Service von NETMF aus (Listing 7).

private static GT.Color[] colors = new GT.Color[]
  { GT.Color.Red, GT.Color.Green, GT.Color.Blue };

void AzureServiceGet()
{
  string url = string.Concat(
    MobileService.ApplicationUri,
    "tables/ColorChange"
    "?$orderby=TimeStamp%20desc&$top=1");
  
  HttpRequest reqGet =
    HttpHelper.CreateHttpGetRequest(url);
  reqGet.AddHeaderField("X-ZUMO-APPLICATION",
    MobileService.ApplicationKey);

  reqGet.ResponseReceived += reqGet_ResponseReceived;
  reqGet.SendRequest();
}

void reqGet_ResponseReceived(
  HttpRequest sender, HttpResponse response)
{
  System.IO.StreamReader sr =
    new System.IO.StreamReader(response.Stream);
  string json = sr.ReadToEnd();

  JsonSerializer serializer =
    new JsonSerializer(DateTimeFormat.ISO8601);
  System.Collections.ArrayList ColorChanges =
    JsonSerializer.DeserializeString(json)
      as System.Collections.ArrayList;

  if(ColorChanges.Count > 0)
  {
    ColorChange colorChange =
      new ColorChange(ColorChanges[0] as Hashtable);
    led.TurnColor(colors[colorChange.Color]);
  }
}

void AzureServicePost()
{
  int color = 0;
  GT.Color c = led.GetCurrentColor();
  if (c == GT.Color.Red) color = 0;
  else if (c == GT.Color.Green) color = 1;
  else if(c == GT.Color.Blue) color = 2;

  ColorChange colorChange = new ColorChange()
    { TimeStamp = DateTime.Now, Color = color };
  JsonSerializer serializer =
    new JsonSerializer(DateTimeFormat.ISO8601);
  string json = serializer.Serialize(colorChange);

  POSTContent postContent =
    POSTContent.CreateTextBasedContent(json);

  string url = string.Concat(
    MobileService.ApplicationUri,
    "tables/ColorChange");
  HttpRequest reqPost =
    HttpHelper.CreateHttpPostRequest(
    url, postContent, "application/json");
  reqPost.AddHeaderField("X-ZUMO-APPLICATION",
    MobileService.ApplicationKey);

  reqPost.ResponseReceived += 
    reqPost_ResponseReceived;
  reqPost.SendRequest();
}

void reqPost_ResponseReceived(
  HttpRequest sender, HttpResponse response)
{
  if (response.StatusCode != "201")
  {
    System.IO.StreamReader sr =
      new System.IO.StreamReader(response.Stream);
    string json = sr.ReadToEnd();
  }

  AzureServiceGet();
}

public class ColorChange
{
  public ColorChange() { }
  public ColorChange(System.Collections.Hashtable ht)
  {
    if (ht.Contains("id"))
      Id = (string)ht["id"];
    if (ht.Contains("color"))
      Color = (int)(long)ht["color"];
    if (ht.Contains("timeStamp"))
    {
      var val = ht["timeStamp"];
      TimeStamp =
        Json.NETMF.DateTimeExtensions.FromIso8601(
          val.ToString());
    }
  }

  public string Id { get; set; }
  public DateTime TimeStamp { get; set; }
  public int Color { get; set; }
}

Die interessanten Stellen dabei sind:

  • AzureMobileGet: Nutzt einen Filter als URL-Parameter und fügt den Application Key im Header hinzu.
  • reqGet_ResponseReceived: Liest den JSON String aus der Web Response und deserialisiert diesen in ein Array aus HashTables. Je Datensatz wird dabei eine Hash Table geliefert, die die Spalten enthält; diese wird in ein ColorChange-Objekt konvertiert.
  • AzureServicePost: Serialisiert ein ColorChange-Objekt in JSON und postet diesen an den Azure-Service.
  • class ColorChange: Liest die Spalten einer Hash Table und legt diese in den Eigenschaften des Objekts ab.

Ein Aufruf von AzureServiceGet wird in NetworkAvailable genutzt und ermittelt nach Start des Embedded-Systems die letzte aktuelle Einstellung vom Mobile Service. AzureMobilePost wir in den Event Handler des Schalters eingebaut und persistiert damit die neu eingestellte Farbe in der Cloud.

Ausblick

Für die Nutzung der Azure Mobile Services spricht vor allem eine einfache Umsetzung und die immense Erleichterung bei der Bereitstellung einer Betriebsplattform in der Microsoft-Cloud. Natürlich haben wir in diesem Artikel nicht alle Möglichkeiten der Azure Mobile Services besprochen. Zu erwähnen sind definitiv auch die Push Notifications, die Benachrichtigungen vom Service zum Client erlauben. Meldet sich ein Mobile-Device beim Dienst an, so erhält dieses alle während der Offlinezeit aufgelaufenen Benachrichtigungen zugestellt, ein Pullen kann somit entfallen.

Für alle, die an einer extrem performanten Anbindung zwischen einem Embedded-System und der klassischen Windows-Welt interessiert sind, wird DPWS einen sehr interessanten Ansatz darstellen. DPWS ist das Ebenbild der WCF auf Basis von NETMF/Gadgeteer. Hierüber lassen sich WCF-kompatible Dienste inklusive Callback-Interface realisieren, dies führt im Vergleich zu Azure Mobile Services zu einer noch schlankeren Implementierung mit deutlich höherem Durchsatz. Dafür werden DPWS-basierte Anwendungen nicht so einfach in die Cloud zu bringen sein, und sie sind daher in der Regel nur für rein interne Lösungen geeignet.

Abschließende Worte

Entstanden ist die in diesem Artikel beschriebene Anwendung in insgesamt vier Manntagen während der Projekttage bei artiso. Diese finden in der Regel zweimal jährlich statt, unsere Mitarbeiter können sich hier mit neuen Themen und Technologien beschäftigen. Die Idee: Gehe frei und unbedarft an ein neues Thema heran und schaue was dabei herauskommt. So hatten die an dieser Anwendung beteiligten Akteure vorher zwar gute Kenntnisse in der Windows-Programmierung, Gadgeteer, NETMF, Azure Mobile Services und Universal Applications waren jedoch weitestgehend Neuland. Das Ergebnis spricht ein klares Votum für den vorgestellten Ansatz.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -