Connettere il dispositivo all'acceleratore di soluzioni di monitoraggio remoto (Node.js)

In questa esercitazione viene implementato un dispositivo Chiller che invia i dati di telemetria seguenti all'acceleratore di soluzioni Monitoraggio remoto:

  • Temperatura
  • Pressione
  • Umidità

Per semplicità, il codice genera valori di telemetria di esempio per il chiller. È possibile estendere l'esempio connettendo sensori reali al dispositivo e inviando dati di telemetria reali.

Il dispositivo di esempio inoltre:

  • Invia i metadati alla soluzione per descrivere le proprie funzionalità.
  • Risponde alle azioni attivate dalla pagina Dispositivi della soluzione.
  • Risponde alle modifiche di configurazione inviate dalla pagina Dispositivi della soluzione.

Per completare l'esercitazione, è necessario un account Azure attivo. Se non si dispone di un account Azure, è possibile creare un account di valutazione gratuito in pochi minuti. Per informazioni dettagliate, vedere la pagina relativa alla versione di valutazione gratuita di Azure.

Prima di iniziare

Prima di scrivere il codice per il dispositivo, distribuire l'acceleratore di soluzioni Monitoraggio remoto e aggiungere un nuovo dispositivo reale alla soluzione.

Distribuire l'acceleratore di soluzioni Monitoraggio remoto

Il dispositivo Chiller creato in questa esercitazione invia dati a un'istanza dell'acceleratore di soluzioni Monitoraggio remoto. Se nel proprio account Azure non è già stato effettuato il provisioning dell'acceleratore di soluzioni Monitoraggio remoto, vedere Distribuire l'acceleratore di soluzioni Monitoraggio remoto

Al termine del processo di distribuzione della soluzione Monitoraggio remoto, fare clic su Avvia per aprire il dashboard della soluzione nel browser.

Dashboard della soluzione

Aggiungere il dispositivo alla soluzione Monitoraggio remoto

Nota

Se è già stato aggiunto un dispositivo nella soluzione, è possibile saltare questo passaggio. Per il passaggio successivo sarà però necessaria la stringa di connessione del dispositivo. È possibile recuperare la stringa di connessione di un dispositivo dal portale di Azure oppure usando lo strumento az iot dell'interfaccia della riga di comando.

Per connettere un dispositivo all'acceleratore di soluzioni, è necessario che identifichi se stesso nell'hub IoT mediante delle credenziali valide. Quando si aggiunge il dispositivo alla soluzione, si ha la possibilità di salvare la stringa di connessione del dispositivo che contiene queste credenziali. Le istruzioni per includere la stringa di connessione del dispositivo nell'applicazione client sono illustrate più avanti in questa esercitazione.

Per aggiungere un dispositivo alla soluzione di monitoraggio remoto, completare i passaggi seguenti nella pagina Esplora dispositivi della soluzione:

  1. Scegliere + Nuovo dispositivo, quindi scegliere Fisico come Tipo di dispositivo:

    Aggiungere un dispositivo reale

  2. Immettere Fisico-chiller come ID del dispositivo. Scegliere le opzioni Chiave simmetrica e Genera chiavi automaticamente:

    Scegliere le opzioni per il dispositivo

  3. Scegliere Applica. Prendere quindi nota dei valori di ID dispositivo, Chiave primaria, e Connection string primary key (Chiave primaria della stringa di connessione):

    Recuperare le credenziali

A questo punto è stato aggiunto un dispositivo reale all'acceleratore di soluzioni Monitoraggio remoto e ne è stata annotata la stringa di connessione. Nelle sezioni seguenti si implementerà l'applicazione client che usa la stringa di connessione del dispositivo per connettersi alla soluzione.

L'applicazione client implementa il modello di dispositivo Chiller predefinito. Un modello di dispositivo per l'acceleratore di soluzioni specifica le informazioni seguenti per il dispositivo:

  • Le proprietà che il dispositivo segnala alla soluzione. Ad esempio, un dispositivo Chiller segnala informazioni su firmware e posizione.
  • Tipi di dati di telemetria che il dispositivo invia alla soluzione. Ad esempio, un dispositivo Chiller invia valori di temperatura, umidità e pressione.
  • I metodi che possono essere pianificati nella soluzione per l'esecuzione nel dispositivo. Un dispositivo Chiller, ad esempio, deve implementare i metodi Reboot, FirmwareUpdate, EmergencyValveRelease e IncreasePressure.

Questa esercitazione mostra come connettere un dispositivo reale all'acceleratore di soluzioni di monitoraggio remoto. In questa esercitazione viene usato Node.js, una buona scelta per gli ambienti con vincoli minimi in termine di risorse.

Se si preferisce simulare un dispositivo, vedere Creare e testare un nuovo dispositivo simulato.

Creare una soluzione Node.js

Assicurarsi che nel computer di sviluppo sia installato Node.js 4.0.0 o versioni successive. Per controllare la versione è possibile eseguire node --version nella riga di comando.

  1. Nel computer di sviluppo creare una cartella denominata remotemonitoring. Aprire la cartella nell'ambiente della riga di comando.

  2. Per scaricare e installare i pacchetti necessari per completare l'app di esempio, eseguire i comandi seguenti:

    npm init
    npm install async azure-iot-device azure-iot-device-mqtt --save
    
  3. Nella cartella remotemonitoring creare un file denominato remote_monitoring.js. Aprire il file in un editor di testo.

  4. Nel file remote_monitoring.js, aggiungere le istruzioni require seguenti:

    var Protocol = require('azure-iot-device-mqtt').Mqtt;
    var Client = require('azure-iot-device').Client;
    var Message = require('azure-iot-device').Message;
    var async = require('async');
    
  5. Aggiungere le seguenti dichiarazioni di variabili dopo le istruzioni require . Sostituire il valore segnaposto {device connection string} con il valore annotato per il dispositivo di cui è stato eseguito il provisioning nella soluzione di monitoraggio remoto:

    var connectionString = '{device connection string}';
    
  6. Per definire alcuni dati di telemetria di base, aggiungere le variabili seguenti:

    var temperature = 50;
    var temperatureUnit = 'F';
    var humidity = 50;
    var humidityUnit = '%';
    var pressure = 55;
    var pressureUnit = 'psig';
    
  7. Per definire alcuni valori di proprietà, aggiungere le variabili seguenti:

    var schema = "real-chiller;v1";
    var deviceType = "RealChiller";
    var deviceFirmware = "1.0.0";
    var deviceFirmwareUpdateStatus = "";
    var deviceLocation = "Building 44";
    var deviceLatitude = 47.638928;
    var deviceLongitude = -122.13476;
    var deviceOnline = true;
    
  8. Aggiungere la variabile seguente per definire le proprietà indicate da inviare alla soluzione. Queste proprietà includono i metadati da visualizzare nell'interfaccia utente Web:

    var reportedProperties = {
      "SupportedMethods": "Reboot,FirmwareUpdate,EmergencyValveRelease,IncreasePressure",
      "Telemetry": {
        [schema]: ""
      },
      "Type": deviceType,
      "Firmware": deviceFirmware,
      "FirmwareUpdateStatus": deviceFirmwareUpdateStatus,
      "Location": deviceLocation,
      "Latitude": deviceLatitude,
      "Longitude": deviceLongitude,
      "Online": deviceOnline
    }
    
  9. Per stampare i risultati dell'operazione, aggiungere la funzione helper seguente:

    function printErrorFor(op) {
        return function printError(err) {
            if (err) console.log(op + ' error: ' + err.toString());
        };
    }
    
  10. Aggiungere la funzione helper seguente per impostare in modo casuale i valori di telemetria:

    function generateRandomIncrement() {
        return ((Math.random() * 2) - 1);
    }
    
  11. Aggiungere la funzione generica seguente per gestire le chiamate ai metodi diretti dalla soluzione. La funzione visualizza informazioni sul metodo diretto richiamato, ma in questo esempio non modifica il dispositivo in alcun modo. La soluzione usa metodi diretti per intervenire sui dispositivi:

    function onDirectMethod(request, response) {
      // Implement logic asynchronously here.
      console.log('Simulated ' + request.methodName);
    
      // Complete the response
      response.send(200, request.methodName + ' was called on the device', function (err) {
        if (err) console.error('Error sending method response :\n' + err.toString());
        else console.log('200 Response to method \'' + request.methodName + '\' sent successfully.');
      });
    }
    
  12. Aggiungere la funzione seguente per gestire le chiamate al metodo diretto FirmwareUpdate dalla soluzione. La funzione verifica i parametri passati nel payload del metodo diretto e quindi esegue in modo asincrono una simulazione dell'aggiornamento del firmware:

    function onFirmwareUpdate(request, response) {
      // Get the requested firmware version from the JSON request body
      var firmwareVersion = request.payload.Firmware;
      var firmwareUri = request.payload.FirmwareUri;
    
      // Ensure we got a firmware values
      if (!firmwareVersion || !firmwareUri) {
        response.send(400, 'Missing firmware value', function(err) {
          if (err) console.error('Error sending method response :\n' + err.toString());
          else console.log('400 Response to method \'' + request.methodName + '\' sent successfully.');
        });
      } else {
        // Respond the cloud app for the device method
        response.send(200, 'Firmware update started.', function(err) {
          if (err) console.error('Error sending method response :\n' + err.toString());
          else {
            console.log('200 Response to method \'' + request.methodName + '\' sent successfully.');
    
            // Run the simulated firmware update flow
            runFirmwareUpdateFlow(firmwareVersion, firmwareUri);
          }
        });
      }
    }
    
  13. Aggiungere la funzione seguente per simulare un flusso di aggiornamento del firmware con esecuzione prolungata che segnala lo stato di avanzamento alla soluzione:

    // Simulated firmwareUpdate flow
    function runFirmwareUpdateFlow(firmwareVersion, firmwareUri) {
      console.log('Simulating firmware update flow...');
      console.log('> Firmware version passed: ' + firmwareVersion);
      console.log('> Firmware URI passed: ' + firmwareUri);
      async.waterfall([
        function (callback) {
          console.log("Image downloading from " + firmwareUri);
          var patch = {
            FirmwareUpdateStatus: 'Downloading image..'
          };
          reportUpdateThroughTwin(patch, callback);
          sleep(10000, callback);
        },
        function (callback) {
          console.log("Downloaded, applying firmware " + firmwareVersion);
          deviceOnline = false;
          var patch = {
            FirmwareUpdateStatus: 'Applying firmware..',
            Online: false
          };
          reportUpdateThroughTwin(patch, callback);
          sleep(8000, callback);
        },
        function (callback) {
          console.log("Rebooting");
          var patch = {
            FirmwareUpdateStatus: 'Rebooting..'
          };
          reportUpdateThroughTwin(patch, callback);
          sleep(10000, callback);
        },
        function (callback) {
          console.log("Firmware updated to " + firmwareVersion);
          deviceOnline = true;
          var patch = {
            FirmwareUpdateStatus: 'Firmware updated',
            Online: true,
            Firmware: firmwareVersion
          };
          reportUpdateThroughTwin(patch, callback);
          callback(null);
        }
      ], function(err) {
        if (err) {
          console.error('Error in simulated firmware update flow: ' + err.message);
        } else {
          console.log("Completed simulated firmware update flow");
        }
      });
    
      // Helper function to update the twin reported properties.
      function reportUpdateThroughTwin(patch, callback) {
        console.log("Sending...");
        console.log(JSON.stringify(patch, null, 2));
        client.getTwin(function(err, twin) {
          if (!err) {
            twin.properties.reported.update(patch, function(err) {
              if (err) callback(err);
            });      
          } else {
            if (err) callback(err);
          }
        });
      }
    
      function sleep(milliseconds, callback) {
        console.log("Simulate a delay (milleseconds): " + milliseconds);
        setTimeout(function () {
          callback(null);
        }, milliseconds);
      }
    }
    
  14. Aggiungere il codice seguente per inviare dati di telemetria alla soluzione. L'app client aggiunge proprietà al messaggio per identificare lo schema del messaggio:

    function sendTelemetry(data, schema) {
      if (deviceOnline) {
        var d = new Date();
        var payload = JSON.stringify(data);
        var message = new Message(payload);
        message.properties.add('iothub-creation-time-utc', d.toISOString());
        message.properties.add('iothub-message-schema', schema);
    
        console.log('Sending device message data:\n' + payload);
        client.sendEvent(message, printErrorFor('send event'));
      } else {
        console.log('Offline, not sending telemetry');
      }
    }
    
  15. Aggiungere il codice seguente per creare un'istanza del client:

    var client = Client.fromConnectionString(connectionString, Protocol);
    
  16. Aggiungere il codice seguente a:

    • Aprire la connessione.

    • Impostare un gestore per le proprietà desiderate.

    • Inviare le proprietà segnalate.

    • Registrare i gestori dei metodi diretti. L'esempio usa un gestore separato per il metodo diretto di aggiornamento del firmware.

    • Avviare l'invio dei dati di telemetria.

      client.open(function (err) {
      if (err) {
        printErrorFor('open')(err);
      } else {
        // Create device Twin
        client.getTwin(function (err, twin) {
          if (err) {
            console.error('Could not get device twin');
          } else {
            console.log('Device twin created');
      
            twin.on('properties.desired', function (delta) {
              // Handle desired properties set by solution
              console.log('Received new desired properties:');
              console.log(JSON.stringify(delta));
            });
      
            // Send reported properties
            twin.properties.reported.update(reportedProperties, function (err) {
              if (err) throw err;
              console.log('Twin state reported');
            });
      
            // Register handlers for all the method names we are interested in.
            // Consider separate handlers for each method.
            client.onDeviceMethod('Reboot', onDirectMethod);
            client.onDeviceMethod('FirmwareUpdate', onFirmwareUpdate);
            client.onDeviceMethod('EmergencyValveRelease', onDirectMethod);
            client.onDeviceMethod('IncreasePressure', onDirectMethod);
          }
        });
      
        // Start sending telemetry
        var sendDeviceTelemetry = setInterval(function () {
          temperature += generateRandomIncrement();
          pressure += generateRandomIncrement();
          humidity += generateRandomIncrement();
          var data = {
            'temperature': temperature,
            'temperature_unit': temperatureUnit,
            'humidity': humidity,
            'humidity_unit': humidityUnit,
            'pressure': pressure,
            'pressure_unit': pressureUnit
          };
          sendTelemetry(data, schema)
        }, 5000);
      
        client.on('error', function (err) {
          printErrorFor('client')(err);
          if (sendTemperatureInterval) clearInterval(sendTemperatureInterval);
          if (sendHumidityInterval) clearInterval(sendHumidityInterval);
          if (sendPressureInterval) clearInterval(sendPressureInterval);
          client.close(printErrorFor('client.close'));
        });
      }
      });
      
  17. Salvare le modifiche apportate al file remote_monitoring.js.

  18. Per avviare l'applicazione di esempio, eseguire il comando seguente al prompt dei comandi:

    node remote_monitoring.js
    

Visualizzare la telemetria dei dispositivi

È possibile visualizzare i dati di telemetria inviati dal dispositivo nella pagina Esplora dispositivi della soluzione.

  1. Selezionare il dispositivo del quale è stato effettuato il provisioning nell'elenco dei dispositivi della pagina Esplora dispositivi. Un pannello visualizza le informazioni sul dispositivo, incluso un tracciato dei dati di telemetria del dispositivo:

    Vedere i dettagli del dispositivo

  2. Scegliere Pressione per modificare la visualizzazione dei dati di telemetria:

    Visualizzare i dati di telemetria per la pressione

  3. Per visualizzare le informazioni di diagnostica sul dispositivo, scorrere fino a Diagnostica:

    Visualizzazione della diagnostica del dispositivo

Agire sul dispositivo

Per richiamare i metodi nei dispositivi, usare la pagina Esplora dispositivi della soluzione di monitoraggio remoto. Nella soluzione di monitoraggio remoto, i dispositivi Chiller implementano ad esempio un metodo Reboot.

  1. Scegliere Dispositivi per passare alla pagina Esplora dispositivi della soluzione.

  2. Selezionare il dispositivo del quale è stato effettuato il provisioning nell'elenco dei dispositivi della pagina Esplora dispositivi:

    Selezionare il dispositivo reale

  3. Per visualizzare un elenco dei metodi che è possibile chiamare per un dispositivo, scegliere Jobs (Processi), quindi Metodi. Per pianificare un processo in modo che venga eseguito su più dispositivi, è possibile selezionare più dispositivi nell'elenco. Il pannello Jobs (Processi) visualizza i tipi di metodi comuni a tutti i dispositivi selezionati.

  4. Scegliere Reboot, impostare il nome processo su RebootPhysicalChiller e quindi fare clic su Applica:

    Pianificare l'aggiornamento del firmware

  5. Nella console che esegue il codice del dispositivo mentre il dispositivo simulato gestisce il metodo viene visualizzata una sequenza di messaggi.

Nota

Per tenere traccia dello stato del processo nella soluzione, scegliere View Job Status (Visualizza stato del processo).

Passaggi successivi

L'articolo Personalizzare l'acceleratore di soluzioni Monitoraggio remoto descrive alcuni modi per personalizzare l'acceleratore di soluzioni.