Translated by Marco Minerva from the English version by Michael Dodaro
Questo esempio mostra come inviare messaggi da un’applicazione .NET Gadgeteer verso un Web service REST che registra i dati sull’attivazione di un sensore. In questo caso il sensore è il Light Sensor implementato in uno dei precedenti post. Il messaggio è semplicemente un identificativo dell’account del sensore e la data e l’ora di attivazione. Il codice è però estendibile per supportare vari tipi di device e notifiche.
Inviare le notifiche al Web Service come una richiesta POST
Questo codice estende lo scenario di test descritto nella sezione Utilizzare gli eventi generati dal nuovo modulo nell’articolo relativo al Light Sensor di cui abbiamo già parlato.
Il codice di test del sensore è stato adattato per inviare una notifica a un servizio Web quando il sensore è attivato. Nel gestore dell’evento LightSensorHigh, utilizziamo il metodo statico CreateHttpPostRequest di HttpHelper per creare la richiesta al Web service. L’identificatore dell’account è specificato nell’URL, il cui template è descritto nell’implementazione del servizio Web mostrato più avanti. Questo esempio non ha bisogno di informazioni nel corpo della richiesta POST, quindi il parametro POSTContent è creato senza contenuto. Il Content Type è nullo. Si tratta di un metodo asincrono, che ottiene la risposta nel metodo delegato, HttpRequest.ResponseReceived, assegnato prima di chiamare HttpRequest.SendRequest.
using System; using Microsoft.SPOT; using Microsoft.SPOT.Presentation; using Microsoft.SPOT.Presentation.Controls; using Microsoft.SPOT.Presentation.Media; using GT = Gadgeteer; using GTM = Gadgeteer.Modules; using Gadgeteer.Modules.GHIElectronics; using Gadgeteer.Modules.IntegralDesign; using System.Net; namespace TestLightSensor { public partial class Program { void ProgramStarted() { lightSensor.LightSensorHigh += new LightSensor.LightSensorEventHandler(lightSensor_LightSensorHigh); lightSensor.LightSensorLow += new LightSensor.LightSensorEventHandler(lightSensor_LightSensorLow); led.TurnRed(); ethernet.UseDHCP(); ethernet.NetworkUp += new GTM.Module.NetworkModule.NetworkEventHandler(ethernet_NetworkUp); ethernet.NetworkDown += new GTM.Module.NetworkModule.NetworkEventHandler(ethernet_NetworkDown); Debug.Print("Program Started"); } void lightSensor_LightSensorHigh(LightSensor sender, LightSensor.LightSensorState state) { led.BlinkRepeatedly(GT.Color.White); POSTContent emptyPost = new POSTContent(); var req = HttpHelper.CreateHttpPostRequest("http://integral-data.com/SensorData/Sensor/TestREST", emptyPost, null); req.ResponseReceived += new HttpRequest.ResponseHandler(req_ResponseReceived); req.SendRequest(); } void req_ResponseReceived(HttpRequest sender, HttpResponse response) { if (response.StatusCode != "200") Debug.Print(response.StatusCode); led.TurnOff(); } void ethernet_NetworkDown(GTM.Module.NetworkModule sender, GTM.Module.NetworkModule.NetworkState state) { led.TurnRed(); } void ethernet_NetworkUp(GTM.Module.NetworkModule sender, GTM.Module.NetworkModule.NetworkState state) { led.TurnGreen(); } void lightSensor_LightSensorLow(LightSensor sender, LightSensor.LightSensorState state) { led.TurnOff(); } } }
L’implementazione del Web Service per registrare i dati del sensore.
Il Web Service REST per gestire i dati provenienti dal sensore è implementato utilizzando il Visual Studio WCF REST Service Template 4. Creare un Web Service REST con questo template è molto semplice, ma alcuni dettagli della sua configurazione possono essere complessi. Innanzi tutto, scarichiamo e installiamo il template attraverso l’opzione Visual Studio->New Project->Online Templates.
La maggior parte del codice del progetto è contenuto nel file Service1.cs. Cambiamo il nome della classe in SensorData. Eliminiamo il file SampleItem.cs perché utilizzeremo un Entity Data Model di ADO .NET per gestire i dati raccolti dal servizio. L’Entity Framework consente di evitare l’utilizzo di query SQL nel proprio codice; se non lo avete mai usato, questo esempio mostra un semplice scenario del suo funzionamento. Per i nostri scopi, utilizzeremo SQL Server Compact Edition, ma non è necessario conoscere la sintassi SQL per implementare il servizio.
Aggiungiamo un SQL Server Compact 4.0 Local Database al progetto utilizzando il comando Add->New Item dal Solution Explorer. Chiamiamo il database SensorDatabase ed inseriamolo nella cartella App_Data, come consigliato dal wizard. Facciamo doppio clic sul file nel Solution Explorer per aprire il Server Explorer.
Le informazioni che memorizzeremo sono formate da una chiave primaria per ogni riga, il nome dell’account e l’orario in cui il sensore è attivato. La tabella che contiene questi dati avrà una colonna per ognuno degli elementi citati. All’interno del Server Explorer, facciamo clic sull’icona Tables e aggiungiamo una nuova tabella di nome SensorDataTable. Creiamo una colonna di nome Id, tipo intero, che non ammette i valori nulli, è univoca ed è la chiave primaria della tabella. Configuriamola come Identity column con Increment e Seed uguali ad 1. Creiamo quindi una seconda colonna SensorAccount, con tipo nvarchar(20), che non ammette valori nulla, non è univoca né colonna identità. Infine, aggiungiamo una colonna con nome ActivationTime, di tipo DateTime, anch’essa che non ammette valori nulli, non univoca né identità.
Ora che abbiamo definito dove salvare i dati, possiamo implementare il data model che fornirà l’accesso a questi dati via codice. Facciamo clic con il tasto destro del mouse sul progetto nel Solution Explorer e aggiungiamo un ADO .NET Entity Data Model, chiamato SensorDataModel. Nella successiva pagina del wizard selezioniamo l’opzione Generate from database. Il database SensorDatabase dovrebbe essere automaticamente selezionato nella casella di riepilogo della pagina seguente, così come la stringa di connessione dovrebbe essere già inserita. Assicuriamoci che l’opzione Save entity connection string settings in Web.config sia selezionata. Manteniamo il nome suggerito, SensorDatabaseEntities, e facciamo clic su Next. Selezioniamo la casella Tables nell’ultima pagina del wizard e confermiamo con un clic sul pulsante Finish.
Dopo l’apertura del designer per il file SensorDataModel.edmx, nella finestra delle proprietà rinominiamo la proprietà SensorDataTables in SensorDataItems e SensorDataTable in SensorDataItem. L’oggetto SensorDataItem che vediamo nel designer rappresenta l’entità che contiene i risultati di una singola richiesta inviata al Web Service quando il sensore viene attivatao. Salviamo il file SensorDataModel.edmx.
Implementazione del template per l’URL
Nel file SensorData.cs commentiamo o cancelliamo la lista di SampleItems, come mostrato nella riga seguente: come abbiamo descritto prima, infatti, il nostro esempio salva i dati in un database e utilizza l’Entity Data Model di ADO .NET.
// TODO: Implement the collection resource that will contain the SampleItem instances //System.Collections.Generic.List SensorDataList = new List();
Il WCF Rest Service Template 40 rende molto semplice la creazione di URL template che definiscono i path del Web Service. Gli attributi WebGet e WebInvoke permettono di specificare i metodi HTML di GET, PUT, POST e DELETE. E’ necessaria un po’ di pratica per capire il funzionamento del sistema, ma l’utilizzo del template semplifica molto il lavoro. Le implementazioni di richieste GET hanno due varianti, una per ottenere la collezione di tutte le informazioni sui sensori e l’altra per ottenere solo un singolo account.
Di seguito vediamo il metodo per ottenere i dati di tutti i sensori. Il codice base del template è lasciato commentato così è più facile capire dove andare a scrivere la nostra implementazione. Notiamo l’attributo [WebGet(UriTemplate = “Data”)] che specifica il path dopo il nome di dominio per l’invocazione del metodo relativo. Questa funzione restituisce la lista di tutti i SensorDataItem contenuti nel database, ordinati secondo il valore di ActivationTime.
[WebGet(UriTemplate = "Data")] public List<SensorDataItem> GetCollection() { // TODO: Replace the current implementation to return a collection of SampleItem instances // return new List<SampleItem>() { new SampleItem() { Id = 1, StringValue = "Hello" } }; using (SensorDatabaseEntities objectContext = new SensorDatabaseEntities()) { return objectContext.SensorDataItems.OrderBy(SensorData => SensorData.ActivationTime).ToList(); } }
Il metodo POST che crea una nuova istanza di SensorDataItem usa l’attributo [WebInvoke(UriTemplate = “Sensor/{account”}]. Questa sintassi indica l’elemento del path “Sensor” e un parametro variabile che specifica il nome dell’account del sensore. Di seguito si riporta l’implementazione completa.
The POST method that creates a new instance of SensorDataItem uses [WebInvoke(UriTemplate = “Sensor/{account”}]. This syntax indicates the path item “Sensor” and a variable parameter that specifies the sensor account name. The complete method implementation follows.
[WebInvoke(UriTemplate = "Sensor/{account}", Method = "POST")] public SensorDataItem Create(SensorDataItem instance, string account) { using (SensorDatabaseEntities objectContext = new SensorDatabaseEntities()) { SensorDataItem newDatum = new SensorDataItem(); newDatum.ActivationTime = DateTime.Now; newDatum.SensorAccount = account; objectContext.SensorDataItems.AddObject(newDatum); objectContext.SaveChanges(); return newDatum; } }
Avvio del servizio web e registrazione dei dati del sensore
I due metodi appena illustrati sono sufficienti per avviare il Web Service. Potrebbe essere necessario inserire la seguente configurazione nel file Web.config per testare il servizio utilizzando il Visual Studio Development Server.
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
Notiamo che il test con il web server integrato di Visual Studio potrebbe dare risultati diversi rispetto ad un hosting reale. E’ possibile fare riferimento a questo articolo di Rick Anderson se la nostra applicazione funziona nell’ambiente di test, ma non una volta pubblicata in remoto. In particolare, SQL Server Compact Edition richiede alcune librerie che potrebbero non risiedere sul server dell’host. In questo caso, facciamo clic destro sul progetto nel Solution Explorer e seleziamo SQL Server Compact in Add Deployable Dependencies. Potrebbe anche essere necessario disabilitare la Basic Authentication di ISS per il progetto.
Il modo più facile per provare il metodo POST consiste nell’utilizzare il codice che abbiamo scritto all’inizio, nella sezione Inviare le notifiche al Web Service come una richiesta POST.
#1 by Michael Dodaro on January 20, 2012 - 5:00 PM
Ho aggiornato questo esempio per utilizzare i nuovi metodi Gadgeteer.Networking.HttpHelper. La classe POSTContent invia la richiesta in modo asincrono in modo da non bloccare il dispatcher mentre la richiesta HTTP è eccezionale. Il processo è più semplice da usare e più efficiente.