Luglio 2015

Volume 30 numero 7

Azure Insider - evento mozzi per Analitica e la visualizzazione, parte 3

Da Bruno Terkaly | Luglio 2015

Se hai seguito questa serie, infine si vedrà la realizzazione dell'obiettivo principale del precedente lavoro in questa puntata finale — per visualizzare i dati provenienti da dispositivi di Raspberry Pi nel campo. Questo è il capitolo finale di una serie di tre parti intorno uno scenario Internet of Things (IoT). Per i precedenti due articoli, controlla il numero di aprile (msdn.microsoft.com/magazine/dn948106) e il numero di giugno (msdn.microsoft.com/magazine/mt147243).

Gli obiettivi di alto livello per mese questo sono abbastanza semplici. Farò costruire un'applicazione di node. js che agisce come un sito Web e fornisce i dati al dispositivo mobile, sarà quindi visualizzato un grafico a barre dei dati pluviometrici. Ho bisogno del server Web Node. js perché generalmente non è consigliabile per un dispositivo mobile per accedere direttamente agli archivi di dati.

Altre tecnologie potrebbero avere eseguito questa funzione, ad esempio ASP.NET Web API o applicazioni API di Microsoft Azure. Maggior parte degli architetti vorrei consigliarvi di filtrare, ordinare e analizzare i dati nel livello intermedio su un server, non sul dispositivo mobile stesso. Ecco perché i dispositivi mobili hanno meno potenza di calcolo e non volete inviare grandi quantità di informazioni attraverso la rete al dispositivo. In breve, lasciare che il server fare il lavoro pesante e lasciare che il dispositivo di visualizzazione grafica.

Per iniziare

Per eseguire gli esempi in mese questo, è necessario avere completato le precedenti due da problemi di aprile e giugno. Se non lo hai, probabilmente è possibile ottenere con manualmente costruendo il proprio archivio di dati di DocumentDB.

Supponendo che hai precedentemente creato un archivio di dati di DocumentDB, iniziare andando sul portale di Azure e visualizzazione del database di DocumentDB. C'è qualche bella utensili presso il portale che ti permette di visualizzare i dati ed eseguire query diverse. Una volta si convalidano i dati c' sono e capire la sua struttura di base, è possibile iniziare a creare il server Web Node. js.

Gli sviluppatori in genere come convalidare il software come costruiscono fuori. Un sacco di sviluppatori iniziare con unit test, ad esempio, con strumenti quali Mocha o gelsomino. Ad esempio, prima di costruire il client mobile, ha senso per garantire che il server Web Node. js funziona come previsto. Un approccio consiste nell'utilizzare uno strumento di proxy Web, ad esempio Fiddler. Che lo rende facile da emettere richieste Web (ad esempio HTTP GET) e Mostra la risposta in formato nativo JSON. Questo può essere utile, perché quando si compila il client mobile, si può essere sicuri che tutti i problemi sono correlati al client mobile, non il servizio Web.

Per mantenere le cose semplici, è possibile utilizzare un dispositivo Windows Phone come il client, anche se i dispositivi iOS e Android sono più prevalenti. Forse l'approccio migliore sarebbe una tecnologia basata su Web. Questo lascerebbe consumano o visualizzare i dati da qualsiasi dispositivo, quindi non limitarsi ai dispositivi mobili. Un altro approccio consiste nell'utilizzare nativi prodotti multipiattaforma, come Xamarin.

Ci sono diverse fonti di informazioni che è possibile utilizzare per compilare un client mobile basato su Web, non ultimo dei quali è il corpo di standard W3C. Potete leggere di più su di loro nei documenti standard a bit.ly/1PQ6uOt. Apache Software Foundation ha anche fatto un sacco di lavori in questo spazio. Per saperne di più riguardo alle cordova.apache.org. Mi sono concentrato su Windows Phone qui perché il codice è semplice, facile da eseguire il debug e semplice da spiegare.

DocumentDB al portale

In questa sezione successiva, costruirò il lavoro precedente, in cui ho creato un archivio di dati di DocumentDB per i dati di temperatura e di città. Per trovare il database di DocumentDB (TemperatureDB), è sufficiente fare clic sulla voce di menu, Sfoglia, quindi selezionare account di DocumentDB. Si potrebbe anche aggiungere il database come una tegola sulla pagina principale cliccando col tasto destro, rendendo il principale portale di Azure pagina un dashboard. Potete trovare un bel riassunto di utilizzando il portale utensili per interagire con il vostro archivio di dati di DocumentDB in bit.ly/112L4X1.

Una delle caratteristiche veramente utile presso il nuovo portale è la possibilità di eseguire query e visualizzare i dati effettivi in DocumentDB, permettendo di vedere la città e temperatura dati in formato JSON nativo. Questo semplifica notevolmente la vostra capacità di modificare facilmente il server Web Node. js e creare qualsiasi query necessarie.

Costruire il server Web Node. js

Una delle prime cose che devi fare quando la creazione di server Web Node. js è connettersi all'archivio dati DocumentDB. Troverete le stringhe di connessione necessari sul portale di Azure. Per raggiungere le stringhe di connessione dal portale, selezionare l'archivio di TemperatureDB DocumentDB, quindi fare clic su tutte le impostazioni, seguita da chiavi.

Da lì, allora avrai bisogno di due informazioni. Il primo è l'URI per l'archivio di dati di DocumentDB. Il secondo è la sicurezza delle informazioni chiave (detta chiave primaria presso il portale), che consente di imporre l'accesso sicuro dal server Web di node. js. Vedrete la connessione e le informazioni del database nel codice seguente:

{
  "HOST"       : "https://temperaturedb.documents.azure.com:443/",
  "AUTH_KEY"   : "secret key from the portal",
  "DATABASE"   : "TemperatureDB",
  "COLLECTION" : "CityTempCollection"
}

Il server Web Node. js leggerà le informazioni di configurazione. Il campo host nel file di configurazione sarà diverso, perché tutti gli URI sono univoci. La chiave di autorizzazione è anche unica.

Nei precedenti articoli, il database di temperatura è stato denominato TemperatureDB. Ogni database può contenere più raccolte, ma in questo caso c'è solo uno chiamato la raccolta CityTemperature. Una raccolta non è altro che un elenco di documenti. In questo modello di dati, un singolo documento è una città con 12 mesi di dati di temperatura.

Come ci si immerge nei dettagli del codice per il server Web di node. js, è possibile usufruire dell'ampio ecosistema del componente aggiuntivo librar­ies per node. js. Si utilizzerà due librerie (chiamate nodo pacchetti) per questo progetto. Il primo pacchetto è per la funzionalità di DocumentDB. Il comando per installare il pacchetto DocumentDB è: NPM installare documentdb. Il secondo pacchetto è per la lettura del file di configurazione: NPM installare nconf. Questi pacchetti forniscono funzionalità aggiuntive mancante nell'installazione predefinita di node. js. Potete trovare un tutorial più ampio sull'edificio fuori un'applicazione Node. js per DocumentDB nella documentazione di Azure al bit.ly/1E7j5Wg.

Ci sono sette sezioni nel server Web Node. js, come mostrato Figura 1. Sezione 1 copre collegamenti ad alcuni dei pacchetti installati per avere accesso ad essi più avanti nel codice. Sezione 1 definisce anche la porta di default a cui si connetterà il client mobile. Quando distribuito in Azure, il numero di porta è gestito da Azure, quindi costruire il process.env.port.

Figura 1 il costruito su Server Web Node. js

// +-----------------------------+
// |        Section 1            |
// +-----------------------------+
var http = require('http');
var port = process.env.port || 1337;
var DocumentDBClient = require('documentdb').DocumentClient;
var nconf = require('nconf');
// +-----------------------------+
// |        Section 2            |
// +-----------------------------+
// Tell nconf which config file to use
nconf.env();
nconf.file({ file: 'config.json' });
// Read the configuration data
var host = nconf.get("HOST");
var authKey = nconf.get("AUTH_KEY");
var databaseId = nconf.get("DATABASE");
var collectionId = nconf.get("COLLECTION");
// +-----------------------------+
// |        Section 3            |
// +-----------------------------+
var client = new DocumentDBClient(host, { masterKey: authKey });
// +-----------------------------+
// |        Section 4            |
// +-----------------------------+
http.createServer(function (req, res) {
  // Before you can query for Items in the document store, you need to ensure you
  // have a database with a collection, then use the collection
  // to read the documents.
  readOrCreateDatabase(function (database) {
    readOrCreateCollection(database, function (collection) {
      // Perform a query to retrieve data and display
      listItems(collection, function (items) {
        var userString = JSON.stringify(items);
        var headers = {
          'Content-Type': 'application/json',
          'Content-Length': userString.length
        };
        res.write(userString);
        res.end();
      });
    });
  });
}).listen(8124,'localhost');  // 8124 seemed to be the
                              // port number that worked
                              // from my development machine.
// +-----------------------------+
// |        Section 5            |
// +-----------------------------+
// If the database does not exist, then create it, or return the database object.
// Use queryDatabases to check if a database with this name already exists. If you
// can't find one, then go ahead and use createDatabase to create a new database
// with the supplied identifier (from the configuration file) on the endpoint
// specified (also from the configuration file).
var readOrCreateDatabase = function (callback) {
  client.queryDatabases('SELECT * FROM root r WHERE r.id="' + databaseId +
    '"').toArray(function (err, results) {
    console.log('readOrCreateDatabase');
    if (err) {
      // Some error occured, rethrow up
      throw (err);
    }
    if (!err && results.length === 0) {
      // No error occured, but there were no results returned,
      // indicating no database exists matching the query.           
      client.createDatabase({ id: databaseId }, function (err, createdDatabase) {
        console.log('client.createDatabase');
        callback(createdDatabase);
      });
    } else {
      // we found a database
      console.log('found a database');
      callback(results[0]);
    }
  });
};
// +-----------------------------+
// |        Section 6            |
// +-----------------------------+
// If the collection does not exist for the database provided, create it,
// or return the collection object. As with readOrCreateDatabase, this method
// first tried to find a collection with the supplied identifier. If one exists,
// it is returned and if one does not exist it is created for you.
var readOrCreateCollection = function (database, callback) {
  client.queryCollections(database._self, 'SELECT * FROM root r WHERE r.id="' +
    collectionId + '"').toArray(function (err, results) {
    console.log('readOrCreateCollection');
    if (err) {
      // Some error occured, rethrow up
      throw (err);
    }
    if (!err && results.length === 0) {
      // No error occured, but there were no results returned, indicating no
      // collection exists in the provided database matching the query.
      client.createCollection(database._self, { id: collectionId },
        function (err, createdCollection) {
        console.log('client.createCollection');
        callback(createdCollection);
      });
    } else {
      // Found a collection
      console.log('found a collection');
      callback(results[0]);
    }
  });
};
// +-----------------------------+
// |        Section 7            |
// +-----------------------------+
// Query the provided collection for all non-complete items.
// Use queryDocuments to look for all documents in the collection that are
// not yet complete, or where completed = false. It uses the DocumentDB query
// grammar, which is based on ANSI - SQL to demonstrate this familiar, yet
// powerful querying capability.
var listItems = function (collection, callback) {
  client.queryDocuments(collection._self, 'SELECT c.City,
    c.Temperatures FROM c where c.id="WACO- TX"').toArray(function (err, docs) {
    console.log('called listItems');
    if (err) {
      throw (err);
    }
    callback(docs);
  });
}

Sezione 2 legge dal file di config, che contiene le informazioni di connessione, compresa la raccolta di database e documenti. E ' sempre senso prendere i valori letterali stringa relativa alle informazioni di connessione e metterli separatamente in un file di configurazione.

La sezione 3 è l'oggetto di connessione client che consente di interagire con DocumentDB. La connessione viene passata al costruttore per DocumentDBClient.

Sezione 4 rappresenta il codice eseguito una volta che il client mobile si connette all'applicazione Server Web Node. js. Il createServer è un nucleo primitivo per applicazioni di node. js e coinvolge un numero di concetti intorno il ciclo degli eventi e l'elaborazione delle richieste HTTP. Potete leggere di più su tale costrutto (bit.ly/1FcNq1E).

Questo rappresenta il punto di ingresso ad alto livello per i client si connettono al server Web di node. js. Inoltre è responsabile della chiamata di altre parti di codice di node. js che recupera dati JSON da DocumentDB. Una volta che i dati vengono recuperati, sarà confezionarlo come payload JSON-based e consegnarlo a un client mobile. Sfrutta gli oggetti richiesta e risposta, che sono i parametri della funzione createServer, (http.createServer (funzione (req, res)...).

Sezione 5 inizia l'elaborazione di query DocumentDB. DocumentDB archivio dati può contenere più database. Lo scopo della sezione 5 è quello di restringere i dati all'URI DocumentDB e puntare a un database specifico. In questo caso, che è TemperatureDB. Vedrete anche alcuni codice extra che non viene utilizzato direttamente, ma c'è puramente a scopo didattico. Noterete anche qualche codice per creare un database se uno non esiste. Gran parte della logica nella sezione 5 e oltre è basata sul DocumentDB npm pacchetto installato in precedenza.

Sezione 6 rappresenta il passo successivo nel processo di recupero di dati. Il codice qui viene chiamato automaticamente come risultato il codice nella sezione 5. Sezione 6 ulteriormente si restringe i dati, il drill-down l'insieme dei documenti utilizzando il database stabilito (temperatura­DB) nella sezione 5. Si noterà l'istruzione select che include una dove clausola per la raccolta di CityTemperature. Che include codice per creare una raccolta se non ne esiste uno.

Sezione 7 rappresenta la query finale eseguita prima dei dati viene restituiti al client mobile. Per mantenere le cose semplici, la query è hardcoded per restituire i dati di temperatura per la città di Waco, Texas. In uno scenario realistico, il client mobile sarebbe passare in città (basata su input dell'utente o, eventualmente, la posizione del dispositivo). Il server Web Node. js sarebbe quindi analizzare la città passata e aggiungerli a Where clausola nella sezione 7.

Il server Web Node. js è ora completo e pronto per l'esecuzione. Una volta è fino e in esecuzione, attenderà all'infinito per le richieste dei client dal dispositivo mobile. Si incorrerà nel server Web di node. js localmente sul computer di sviluppo. A questo punto, ha senso utilizzare Fiddler per iniziare a testare il server Web Node. js. Fiddler ti permette di inviare richieste HTTP (in questo caso, GET) per il servizio Web e visualizzare la risposta. Convalida il comportamento in Fiddler può aiutarvi a risolvere eventuali problemi prima di costruire il client mobile.

Ora sei pronto per compilare il client mobile, che comprende due componenti architettonici base — XAML UI e il codice di CS -­dietro (dove vive la logica di programmazione). Il codice mostrato Figura 2 rappresenta il markup per l'interfaccia visiva sul client mobile.

Figura 2 il Markup XAML per barra Mobile Client principale schermo grafico

<Page
  x:Class="CityTempApp.MainPage"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="using:CityTempApp"
  xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d"
  xmlns:charting="using:WinRTXamlToolkit.Controls.DataVisualization.Charting"
  Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <Grid>
    <!-- WinRTXamlToolkit chart control -->
    <charting:Chart
      x:Name="BarChart"
      Title=""
      Margin="5,0">
      <charting:BarSeries
        Title="Rain (in)"
        IndependentValueBinding="{Binding Name}"
        DependentValueBinding="{Binding Value}"
        IsSelectionEnabled="True"/>
      </charting:Chart>
  </Grid>
</Page>

Si noti che incorpora il WinRTXamlToolkit, che potete trovare su CodePlex a bit.ly/1PQdXwO. Questo toolkit include un numero di controlli interessanti. Quello che userai è il controllo chart. Per i dati del grafico, semplicemente costruire un insieme nome/valore e allegarlo al controllo. In questo caso, potrai creare una collezione di nome/valore di dati pluviometrici per ogni mese in una data città.

Prima di presentare la soluzione finale, ci sono alcune avvertenze. Si potrebbe contestare utilizzando un'applicazione nativa di Windows Phone, a favore di un approccio maggiormente basato su Web.

Il client mobile costruito qui prende una serie di scorciatoie per venire con la funzionalità minima nuda. Ad esempio, il grafico a barre appariranno immediatamente una volta che si esegue il client di Windows Phone. Ecco perché c'è una richiesta Web al server Web di node. js dall'evento OnNavigatedTo. Questo viene eseguito automaticamente una volta che viene avviato il client di Windows Phone. Si può vedere questo nella sezione 1 del codice client mobile mostrato Figura 3.

Figura 3 codice per il Client Mobile

public sealed partial class MainPage : Page
{
  public MainPage()
  {
    this.InitializeComponent();
    this.NavigationCacheMode = NavigationCacheMode.Required;
  }
  // +-----------------------------+
  // |        Section 1            |
  // +-----------------------------+
  protected override void OnNavigatedTo(NavigationEventArgs e)
  {
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
      "http://localhost:8124");
    request.BeginGetResponse(MyCallBack, request);
  }
  // +-----------------------------+
  // |        Section 2            |
  // +-----------------------------+
  async void MyCallBack(IAsyncResult result)
  {
    HttpWebRequest request = result.AsyncState as HttpWebRequest;
    if (request != null)
    {
      try
      {
        WebResponse response = request.EndGetResponse(result);
        Stream stream = response.GetResponseStream();
        StreamReader reader = new StreamReader(stream);
        JsonSerializer serializer = new JsonSerializer();
        // +-----------------------------+
        // |        Section 3            |
        // +-----------------------------+
        // Data structures coming back from Node
        List<CityTemp> cityTemp = (List<CityTemp>)serializer.Deserialize(
          reader, typeof(List<CityTemp>));
        // Data structure suitable for the chart control on the phone
        List<NameValueItem> items = new List<NameValueItem>();
        string[] months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
        for (int i = 11; i >= 0; i-- )
        {
          items.Add(new NameValueItem { Name = months[i], Value =
            cityTemp[0].Temperatures[i] });
        }
        // +-----------------------------+
        // |        Section 4            |
        // +-----------------------------+
        // Provide bar chart the data
        await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
          this.BarChart.Title = cityTemp[0].City + ", 2014";
          ((BarSeries)this.BarChart.Series[0]).ItemsSource = items;
        });
      }
      catch (WebException e)
      {
        return;
      }
    }
  }
}
// +-----------------------------+
// |        Section 5            |
// +-----------------------------+
// Data structures coming back from Node
public class CityTemp
{
  public string City { get; set;  }
  public List<double> Temperatures { get; set; }
}
// Data structure suitable for the chart control on the phone
public class NameValueItem
{
  public string Name { get; set; }
  public double Value { get; set; }
}

Anche nella sezione 1, noterete che viene effettuata la connessione al server Web Node. js in esecuzione sull'host locale. Ovviamente, è necessario specificare un endpoint diverso se si ospitano server Web Node. js nel cloud pubblico, ad esempio siti Web Azure. Dopo l'emissione della richiesta Web, è impostare il parametro callback utilizzando BeginGetResponse. Le richieste Web sono asincrone, così il codice imposta un callback (MyCallBack). Questo porta alla sezione 2, dove i dati vengono recuperati, elaborati e caricati in un controllo chart.

Il callback nella sezione 2 per la richiesta Web asincrona delineato nella sezione 1 processi il payload inviato indietro dal servizio Web. Il codice elabora la risposta Web, serializzare i dati, che è in formato JSON. La sezione 3 è di trasformare i dati JSON in due strutture di dati separati definite nella sezione 5. L'obiettivo è quello di creare un elenco di nome/valore matrici o elenchi. La classe NameValueItem è la struttura che ha bisogno del controllo chart.

Sezione 4 è circa usando il thread della GUI per assegnare l'elenco di nome/valore al controllo chart. Si può vedere l'assegnazione di elementi nell'insieme di elementi di origine. L'attesa questo. Dispatcher.RunAsync sintassi sfrutta il thread della GUI per l'aggiornamento dei controlli visivi. Il codice non funzionerà correttamente se si tenta di aggiornare l'interfaccia visiva utilizzando lo stesso thread come il trattamento dei dati e la richiesta Web.

Ora è possibile eseguire il client mobile. Si potrebbe mancare alcuni dei dati di temperatura, però, così tutti la barra controlli potrebbero non essere visualizzati.

Avvolgendo

Questo conclude la serie in tre parti, dove ho deciso di mostrare uno scenario IoT-to-end — da ingestione di dati per mantenere i dati per la visualizzazione dei dati. Ho iniziato questa serie ingerendo i dati utilizzando un programma in C in esecuzione sotto Ubuntu, come tentativo di simulare il codice che necessario per l'esecuzione in un dispositivo di Raspberry Pi. È possibile inserire i dati acquisiti da un sensore di temperatura in hub evento Azure. Tuttavia, i dati archiviati qui sono effimeri ed è necessario spostarlo in un archivio permanente.

Che ti ha portato per il secondo articolo, in cui un processo in background preleva i dati da evento mozzi e lo sposta sia Database SQL Azure e DocumentDB. Quindi questa puntata finale esposto questo dati permanentemente memorizzati per dispositivi mobili utilizzando un livello intermedio in esecuzione di node. js.

Ci sono molti potenziali estensioni e miglioramenti che possono essere eseguiti. Ad esempio, un settore che si potrebbe esplorare è la nozione di apprendimento automatico e analitica. La visualizzazione del grafico a barre risponde alla domanda di base, "Cosa è successo?" Una domanda più interessante potrebbe essere: "Cosa succederà?" In altre parole, si può quindi prevedere futuro pioggia? Potrebbe essere l'ultima domanda, "Cosa dovrei io oggi basato su previsioni future?"


Bruno Terkaly è un principal software engineer presso Microsoft con l'obiettivo di consentire lo sviluppo di applicazioni e servizi leader del settore su tutti i dispositivi. Egli è responsabile per la guida superiore cloud e mobile opportunità attraverso gli Stati Uniti e oltre da una prospettiva di tecnologia-abilitazione. Egli aiuta i partner a portare le loro applicazioni al mercato fornendo orientamento architettonico e tecnico profondo impegno durante l'ISV valutazione, sviluppo e distribuzione. Terkaly collabora inoltre strettamente con il cloud e mobile gruppi di ingegneria, fornendo un feedback e che influenzano la tabella di marcia.

Grazie ai seguenti esperti tecnici Microsoft per la revisione di questo articolo: Leland Holmquest, David Magnotti e Nick Trogh
David Magnotti (Microsoft), Leland Holmquest (Microsoft), Nick Trogh (Microsoft)

David Magnotti è un Premier Field Engineer con Microsoft, specializzato nello sviluppo di software. Egli sostiene principalmente i clienti di Microsoft enterprise attraverso costruzione prova dei concetti, lo svolgimento di servizi di consulenza e fornitura laboratori. Egli può essere trovato spesso debug dump di arresto anomalo e analizzando le tracce di prestazioni di windows.

Leland Holmquest è un gestore di soluzione per la piattaforma di Knowledge Management per Microsoft Services, fan di lunga data di MSDN Magazine e sostenitore di do'ers!

Nick Trogh è un Technical Evangelist presso Microsoft, dove egli consente agli sviluppatori di realizzare i loro sogni di software sulla piattaforma Microsoft. Lo si può trovare blogging, tweeting o parlando a eventi per sviluppatori.