Condividi tramite


Esercitazione: Creare e connettere un'applicazione client all'applicazione Azure IoT Central

Importante

Questo articolo include la procedura per connettere un dispositivo usando una firma di accesso condiviso, altrimenti chiamata autenticazione con chiave simmetrica. Questo metodo di autenticazione è comodo per i test e le valutazioni, ma l'autenticazione tramite certificati X.509 rappresenta un approccio più sicuro. Per scoprire di più, vedere Procedure consigliate per la sicurezza > Sicurezza della connessione.

Questa esercitazione illustra come connettere un'applicazione client all'applicazione Azure IoT Central. L'applicazione simula il comportamento di un dispositivo del controller di temperatura. Quando l'applicazione si connette a IoT Central, invia l'ID modello del modello di dispositivo del controller di temperatura. IoT Central usa l'ID modello per recuperare il modello di dispositivo e creare un modello di dispositivo per l'utente. È possibile aggiungere visualizzazioni al modello di dispositivo per consentire a un operatore di interagire con un dispositivo.

In questa esercitazione apprenderai a:

  • Creare ed eseguire il codice del dispositivo per connetterlo all'applicazione IoT Central.
  • Visualizzare i dati di telemetria simulati inviati dal dispositivo.
  • Aggiungere visualizzazioni personalizzate a un modello di dispositivo.
  • Pubblicare il modello di dispositivo.
  • Usare una visualizzazione per gestire le proprietà del dispositivo.
  • Chiamare un comando per controllare il dispositivo.

Esplora il codice

Prerequisiti

Per completare la procedura di questa esercitazione, è necessario quanto segue:

È possibile eseguire questa esercitazione in Linux o in Windows. I comandi della shell in questa esercitazione seguono la convenzione Linux per i separatori di percorso '/'. Se si usa Windows, assicurarsi di sostituire questi separatori con '\'.

I prerequisiti variano in base al sistema operativo:

Linux

Questa esercitazione presuppone l'uso di Ubuntu Linux. I passaggi in questa esercitazione sono stati testati con Ubuntu 18.04.

Per completare questa esercitazione, installare il software seguente nell'ambiente Linux locale:

Installare GCC, Git, cmake e tutte le dipendenze necessarie usando il comando apt-get:

sudo apt-get update
sudo apt-get install -y git cmake build-essential curl libcurl4-openssl-dev libssl-dev uuid-dev

Verificare che la versione di sia maggiore di cmake 2.8.12 e che la versione di GCC sia maggiore di 4.4.7.

cmake --version
gcc --version

Finestre

Per completare questa esercitazione in Windows, installare il software seguente nell'ambiente Windows locale:

Scaricare il codice

In questa esercitazione viene preparato un ambiente di sviluppo che è possibile usare per clonare e compilare Azure IoT Hub SDK per dispositivi per C.

Aprire un prompt dei comandi nella directory di propria scelta. Eseguire questo comando per clonare il repository GitHub di SDK e librerie di Azure IoT per C in tale percorso:

git clone https://github.com/Azure/azure-iot-sdk-c.git
cd azure-iot-sdk-c
git submodule update --init

Il completamento di questa operazione richiederà alcuni minuti.

Esaminare il codice

Nella copia di Microsoft Azure IoT SDK per C scaricata in precedenza aprire i file azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_temperature_controller.c e azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_thermostat_component.c in un editor di testo.

L'esempio implementa il modello di linguaggio di definizione di Temperature Controller digital twin a più componenti.

Quando si esegue l'esempio per connettersi a IoT Central, il dispositivo viene registrato tramite il servizio Device Provisioning e viene generata una stringa di connessione. L'esempio recupera le informazioni necessarie sulla connessione del servizio Device Provisioning dall'ambiente di riga di comando.

In pnp_temperature_controller.c la main funzione chiama CreateDeviceClientAndAllocateComponents per prima cosa:

  • Selezionare l'ID modello di dtmi:com:example:Thermostat;1. IoT Central usa l'ID modello per identificare o generare il modello di dispositivo per questo dispositivo. Per altre informazioni, vedere Assegnare un dispositivo a un modello di dispositivo.
  • Usare il servizio Device Provisioning per effettuare il provisioning del dispositivo e registrarlo.
  • Creare un handle client del dispositivo e connettersi all'applicazione IoT Central.
  • Crea un gestore per i comandi nel componente del controller di temperatura.
  • Crea un gestore per gli aggiornamenti delle proprietà nel componente del controller di temperatura.
  • Crea i due componenti del termostato.

La main funzione successiva:

  • Segnala alcuni valori iniziali delle proprietà per tutti i componenti.
  • Avvia un ciclo per inviare dati di telemetria da tutti i componenti.

La funzione main avvia quindi un thread per inviare periodicamente dati di telemetria.

int main(void)
{
    IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClient = NULL;

    g_pnpDeviceConfiguration.modelId = g_temperatureControllerModelId;
    g_pnpDeviceConfiguration.enableTracing = g_hubClientTraceEnabled;

    // First determine the IoT Hub / credentials / device to use.
    if (GetConnectionSettingsFromEnvironment(&g_pnpDeviceConfiguration) == false)
    {
        LogError("Cannot read required environment variable(s)");
    }
    // Creates the thermostat subcomponents defined by this model.  Since everything
    // is simulated, this setup stage just creates simulated objects in memory.
    else if (AllocateThermostatComponents() == false)
    {
        LogError("Failure allocating thermostat components");
    }
    // Create a handle to device client handle.  Note that this call may block
    // for extended periods of time when using DPS.
    else if ((deviceClient = CreateAndConfigureDeviceClientHandleForPnP()) == NULL)
    {
        LogError("Failure creating Iot Hub device client");
        PnP_ThermostatComponent_Destroy(g_thermostatHandle1);
        PnP_ThermostatComponent_Destroy(g_thermostatHandle2);
    }
    else
    {
        LogInfo("Successfully created device client.  Hit Control-C to exit program\n");

        int numberOfIterations = 0;

        // During startup, send what DTDLv2 calls "read-only properties" to indicate initial device state.
        PnP_TempControlComponent_ReportSerialNumber_Property(deviceClient);
        PnP_DeviceInfoComponent_Report_All_Properties(g_deviceInfoComponentName, deviceClient);
        PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(g_thermostatHandle1, deviceClient);
        PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(g_thermostatHandle2, deviceClient);

        while (true)
        {
            // Wake up periodically to poll.  Even if we do not plan on sending telemetry, we still need to poll periodically in order to process
            // incoming requests from the server and to do connection keep alives.
            if ((numberOfIterations % g_sendTelemetryPollInterval) == 0)
            {
                PnP_TempControlComponent_SendWorkingSet(deviceClient);
                PnP_ThermostatComponent_SendCurrentTemperature(g_thermostatHandle1, deviceClient);
                PnP_ThermostatComponent_SendCurrentTemperature(g_thermostatHandle2, deviceClient);
            }

            IoTHubDeviceClient_LL_DoWork(deviceClient);
            ThreadAPI_Sleep(g_sleepBetweenPollsMs);
            numberOfIterations++;
        }

        // The remainder of the code is used for cleaning up our allocated resources. It won't be executed in this 
        // sample (because the loop above is infinite and is only broken out of by Control-C of the program), but 
        // it is included for reference.

        // Free the memory allocated to track simulated thermostat.
        PnP_ThermostatComponent_Destroy(g_thermostatHandle1);
        PnP_ThermostatComponent_Destroy(g_thermostatHandle2);

        // Clean up the IoT Hub SDK handle.
        IoTHubDeviceClient_LL_Destroy(deviceClient);
        // Free all IoT Hub subsystem.
        IoTHub_Deinit();
    }

    return 0;
}

In pnp_thermostat_component.cla PnP_ThermostatComponent_SendCurrentTemperature funzione mostra come il dispositivo invia i dati di telemetria della temperatura da un componente a IoT Central:

void PnP_ThermostatComponent_SendCurrentTemperature(PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle, IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClient)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;
    IOTHUB_MESSAGE_HANDLE messageHandle = NULL;
    IOTHUB_MESSAGE_RESULT messageResult;
    IOTHUB_CLIENT_RESULT iothubClientResult;

    char temperatureStringBuffer[CURRENT_TEMPERATURE_BUFFER_SIZE];

    // Create the telemetry message body to send.
    if (snprintf(temperatureStringBuffer, sizeof(temperatureStringBuffer), g_temperatureTelemetryBodyFormat, pnpThermostatComponent->currentTemperature) < 0)
    {
        LogError("snprintf of current temperature telemetry failed");
    }
    // Create the message handle and specify its metadata.
    else if ((messageHandle = IoTHubMessage_CreateFromString(temperatureStringBuffer)) == NULL)
    {
        LogError("IoTHubMessage_PnP_CreateFromString failed");
    }
    else if ((messageResult = IoTHubMessage_SetContentTypeSystemProperty(messageHandle, g_jsonContentType)) != IOTHUB_MESSAGE_OK)
    {
        LogError("IoTHubMessage_SetContentTypeSystemProperty failed, error=%d", messageResult);
    }
    else if ((messageResult = IoTHubMessage_SetContentEncodingSystemProperty(messageHandle, g_utf8EncodingType)) != IOTHUB_MESSAGE_OK)
    {
        LogError("IoTHubMessage_SetContentEncodingSystemProperty failed, error=%d", messageResult);
    }
    else if ((messageResult = IoTHubMessage_SetComponentName(messageHandle, pnpThermostatComponent->componentName)) != IOTHUB_MESSAGE_OK)
    {
        LogError("IoTHubMessage_SetContentEncodingSystemProperty failed, error=%d", messageResult);
    }
    // Send the telemetry message.
    else if ((iothubClientResult = IoTHubDeviceClient_LL_SendTelemetryAsync(deviceClient, messageHandle, NULL, NULL)) != IOTHUB_CLIENT_OK)
    {
        LogError("Unable to send telemetry message, error=%d", iothubClientResult);
    }

    IoTHubMessage_Destroy(messageHandle);
}

In pnp_thermostat_component.cla PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property funzione invia un maxTempSinceLastReboot aggiornamento delle proprietà dal componente a IoT Central:

void PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle, IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClient)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;
    char maximumTemperatureAsString[MAX_TEMPERATURE_SINCE_REBOOT_BUFFER_SIZE];
    IOTHUB_CLIENT_RESULT iothubClientResult;

    if (snprintf(maximumTemperatureAsString, sizeof(maximumTemperatureAsString), g_maxTempSinceLastRebootPropertyFormat, pnpThermostatComponent->maxTemperature) < 0)
    {
        LogError("Unable to create max temp since last reboot string for reporting result");
    }
    else
    {
        IOTHUB_CLIENT_PROPERTY_REPORTED maxTempProperty;
        maxTempProperty.structVersion = IOTHUB_CLIENT_PROPERTY_REPORTED_STRUCT_VERSION_1;
        maxTempProperty.name = g_maxTempSinceLastRebootPropertyName;
        maxTempProperty.value =  maximumTemperatureAsString;

        unsigned char* propertySerialized = NULL;
        size_t propertySerializedLength;

        // The first step of reporting properties is to serialize IOTHUB_CLIENT_PROPERTY_WRITABLE_RESPONSE into JSON for sending.
        if ((iothubClientResult = IoTHubClient_Properties_Serializer_CreateReported(&maxTempProperty, 1, pnpThermostatComponent->componentName, &propertySerialized, &propertySerializedLength)) != IOTHUB_CLIENT_OK)
        {
            LogError("Unable to serialize reported state, error=%d", iothubClientResult);
        }
        // The output of IoTHubClient_Properties_Serializer_CreateReported is sent to IoTHubDeviceClient_LL_SendPropertiesAsync to perform network I/O.
        else if ((iothubClientResult = IoTHubDeviceClient_LL_SendPropertiesAsync(deviceClient, propertySerialized, propertySerializedLength,  NULL, NULL)) != IOTHUB_CLIENT_OK)
        {
            LogError("Unable to send reported state, error=%d", iothubClientResult);
        }
        else
        {
            LogInfo("Sending %s property to IoTHub for component %s", g_maxTempSinceLastRebootPropertyName, pnpThermostatComponent->componentName);
        }
        IoTHubClient_Properties_Serializer_Destroy(propertySerialized);
    }
}

In pnp_thermostat_component.cla PnP_ThermostatComponent_ProcessPropertyUpdate funzione gestisce gli aggiornamenti delle proprietà scrivibili da IoT Central:

void PnP_ThermostatComponent_ProcessPropertyUpdate(PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle, IOTHUB_DEVICE_CLIENT_LL_HANDLE deviceClient, const char* propertyName, const char* propertyValue, int version)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;

    if (strcmp(propertyName, g_targetTemperaturePropertyName) != 0)
    {
        LogError("Property %s was requested to be changed but is not part of the thermostat interface definition", propertyName);
    }
    else
    {
        char* next;
        double targetTemperature = strtod(propertyValue, &next);
        if ((propertyValue == next) || (targetTemperature == HUGE_VAL) || (targetTemperature == (-1*HUGE_VAL)))
        {
            LogError("Property %s is not a valid number", propertyValue);
            SendTargetTemperatureResponse(pnpThermostatComponent, deviceClient, propertyValue, PNP_STATUS_BAD_FORMAT, version, g_temperaturePropertyResponseDescriptionNotInt);
        }
        else
        {
            LogInfo("Received targetTemperature %f for component %s", targetTemperature, pnpThermostatComponent->componentName);
            
            bool maxTempUpdated = false;
            UpdateTemperatureAndStatistics(pnpThermostatComponent, targetTemperature, &maxTempUpdated);

            // The device needs to let the service know that it has received the targetTemperature desired property.
            SendTargetTemperatureResponse(pnpThermostatComponent, deviceClient, propertyValue, PNP_STATUS_SUCCESS, version, NULL);
            
            if (maxTempUpdated)
            {
                // If the maximum temperature has been updated, we also report this as a property.
                PnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property(pnpThermostatComponent, deviceClient);
            }
        }
    }
}

In pnp_thermostat_component.cla PnP_ThermostatComponent_ProcessCommand funzione gestisce i comandi chiamati da IoT Central:

void PnP_ThermostatComponent_ProcessCommand(PNP_THERMOSTAT_COMPONENT_HANDLE pnpThermostatComponentHandle, const char *pnpCommandName, JSON_Value* commandJsonValue, IOTHUB_CLIENT_COMMAND_RESPONSE* commandResponse)
{
    PNP_THERMOSTAT_COMPONENT* pnpThermostatComponent = (PNP_THERMOSTAT_COMPONENT*)pnpThermostatComponentHandle;
    const char* sinceStr;

    if (strcmp(pnpCommandName, g_getMaxMinReportCommandName) != 0)
    {
        LogError("Command %s is not supported on thermostat component", pnpCommandName);
        commandResponse->statusCode = PNP_STATUS_NOT_FOUND;
    }
    // See caveats section in ../readme.md; we don't actually respect this sinceStr to keep the sample simple,
    // but want to demonstrate how to parse out in any case.
    else if ((sinceStr = json_value_get_string(commandJsonValue)) == NULL)
    {
        LogError("Cannot retrieve JSON string for command");
        commandResponse->statusCode = PNP_STATUS_BAD_FORMAT;
    }
    else if (BuildMaxMinCommandResponse(pnpThermostatComponent, commandResponse) == false)
    {
        LogError("Unable to build response for component %s", pnpThermostatComponent->componentName);
        commandResponse->statusCode = PNP_STATUS_INTERNAL_ERROR;
    }
    else
    {
        LogInfo("Returning success from command request for component %s", pnpThermostatComponent->componentName);
        commandResponse->statusCode = PNP_STATUS_SUCCESS;
    }
}

Compilare il codice

Usare l'SDK per dispositivi per compilare il codice di esempio incluso:

  1. Creare una sottodirectory cmake nella cartella radice dell'SDK per dispositivi e passare a tale cartella:

    cd azure-iot-sdk-c
    mkdir cmake
    cd cmake
    
  2. Eseguire i comandi seguenti per compilare l'SDK e gli esempi:

    cmake -Duse_prov_client=ON -Dhsm_type_symm_key=ON -Drun_e2e_tests=OFF ..
    cmake --build .
    

Ottenere informazioni di connessione

Quando si eseguirà l'applicazione del dispositivo di esempio più avanti in questa esercitazione, saranno necessari i valori di configurazione seguenti:

  • Ambito ID: nell'applicazione IoT Central passare a Autorizzazioni > Gruppi di connessioni del dispositivo. Prendere nota del valore dell'Ambito ID.
  • Raggruppare la chiave primaria: nell'applicazione IoT Central passare a Autorizzazioni Gruppi > di connessioni > del dispositivo SAS-IoT-Devices. Prendere nota del valore Chiave primaria della firma di accesso condivisa.

Usare Azure Cloud Shell per generare una chiave del dispositivo dalla chiave primaria del gruppo recuperata:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Prendere nota della chiave dispositivo generata. Questo valore verrà usato in un passaggio successivo dell'esercitazione.

Nota

Per eseguire questo esempio, non è necessario registrare il dispositivo in anticipo nell'applicazione IoT Central. L'esempio usa la funzionalità IoT Central per registrare automaticamente i dispositivi quando si connettono per la prima volta.

Eseguire il codice

Per eseguire l'applicazione di esempio, aprire un ambiente di riga di comando e passare alla cartella azure-iot-sdk-c\cmake.

Impostare le variabili di ambiente per configurare l'esempio. Il frammento di codice seguente mostra come impostare le variabili di ambiente al prompt dei comandi di Windows. Se si usa una shell bash, sostituire i comandi set con i comandi export:

set IOTHUB_DEVICE_SECURITY_TYPE=DPS
set IOTHUB_DEVICE_DPS_ID_SCOPE=<The ID scope you made a note of previously>
set IOTHUB_DEVICE_DPS_DEVICE_ID=sample-device-01
set IOTHUB_DEVICE_DPS_DEVICE_KEY=<The generated device key you made a note of previously>
set IOTHUB_DEVICE_DPS_ENDPOINT=global.azure-devices-provisioning.net

Per eseguire l'esempio:

# Bash
cd iothub_client/samples/pnp/pnp_temperature_controller/
./pnp_temperature_controller
REM Windows
cd iothub_client\samples\pnp\pnp_temperature_controller\Debug
.\pnp_temperature_controller.exe

L'output seguente mostra la registrazione del dispositivo e la connessione a IoT Central. L'esempio inizia a inviare dati di telemetria:

Info: Initiating DPS client to retrieve IoT Hub connection information
-> 09:43:27 CONNECT | VER: 4 | KEEPALIVE: 0 | FLAGS: 194 | USERNAME: 0ne0026656D/registrations/sample-device-01/api-version=2019-03-31&ClientVersion=1.6.0 | PWD: XXXX | CLEAN: 1
<- 09:43:28 CONNACK | SESSION_PRESENT: false | RETURN_CODE: 0x0
-> 09:43:29 SUBSCRIBE | PACKET_ID: 1 | TOPIC_NAME: $dps/registrations/res/# | QOS: 1
<- 09:43:30 SUBACK | PACKET_ID: 1 | RETURN_CODE: 1
-> 09:43:30 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $dps/registrations/PUT/iotdps-register/?$rid=1 | PAYLOAD_LEN: 102
<- 09:43:31 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: $dps/registrations/res/202/?$rid=1&retry-after=3 | PACKET_ID: 2 | PAYLOAD_LEN: 94
-> 09:43:31 PUBACK | PACKET_ID: 2
-> 09:43:33 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $dps/registrations/GET/iotdps-get-operationstatus/?$rid=2&operationId=4.2f792ade0a5c3e68.baf0e879-d88a-4153-afef-71aff51fd847 | PAYLOAD_LEN: 102
<- 09:43:34 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: $dps/registrations/res/202/?$rid=2&retry-after=3 | PACKET_ID: 2 | PAYLOAD_LEN: 173
-> 09:43:34 PUBACK | PACKET_ID: 2
-> 09:43:36 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $dps/registrations/GET/iotdps-get-operationstatus/?$rid=3&operationId=4.2f792ade0a5c3e68.baf0e879-d88a-4153-afef-71aff51fd847 | PAYLOAD_LEN: 102
<- 09:43:37 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: $dps/registrations/res/200/?$rid=3 | PACKET_ID: 2 | PAYLOAD_LEN: 478
-> 09:43:37 PUBACK | PACKET_ID: 2
Info: Provisioning callback indicates success.  iothubUri=iotc-60a....azure-devices.net, deviceId=sample-device-01
-> 09:43:37 DISCONNECT
Info: DPS successfully registered.  Continuing on to creation of IoTHub device client handle.
Info: Successfully created device client.  Hit Control-C to exit program

Info: Sending serialNumber property to IoTHub
Info: Sending device information property to IoTHub.  propertyName=swVersion, propertyValue="1.0.0.0"
Info: Sending device information property to IoTHub.  propertyName=manufacturer, propertyValue="Sample-Manufacturer"
Info: Sending device information property to IoTHub.  propertyName=model, propertyValue="sample-Model-123"
Info: Sending device information property to IoTHub.  propertyName=osName, propertyValue="sample-OperatingSystem-name"
Info: Sending device information property to IoTHub.  propertyName=processorArchitecture, propertyValue="Contoso-Arch-64bit"
Info: Sending device information property to IoTHub.  propertyName=processorManufacturer, propertyValue="Processor Manufacturer(TM)"
Info: Sending device information property to IoTHub.  propertyName=totalStorage, propertyValue=10000
Info: Sending device information property to IoTHub.  propertyName=totalMemory, propertyValue=200
Info: Sending maximumTemperatureSinceLastReboot property to IoTHub for component=thermostat1
Info: Sending maximumTemperatureSinceLastReboot property to IoTHub for component=thermostat2
-> 09:43:44 CONNECT | VER: 4 | KEEPALIVE: 240 | FLAGS: 192 | USERNAME: iotc-60a576a2-eec7-48e2-9306-9e7089a79995.azure-devices.net/sample-device-01/?api-version=2020-09-30&DeviceClientType=iothubclient%2f1.6.0%20(native%3b%20Linux%3b%20x86_64)&model-id=dtmi%3acom%3aexample%3aTemperatureController%3b1 | PWD: XXXX | CLEAN: 0
<- 09:43:44 CONNACK | SESSION_PRESENT: false | RETURN_CODE: 0x0
-> 09:43:44 SUBSCRIBE | PACKET_ID: 2 | TOPIC_NAME: $iothub/twin/res/# | QOS: 0 | TOPIC_NAME: $iothub/methods/POST/# | QOS: 0
-> 09:43:44 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: devices/sample-device-01/messages/events/ | PACKET_ID: 3 | PAYLOAD_LEN: 19
-> 09:43:44 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: devices/sample-device-01/messages/events/%24.sub=thermostat1 | PACKET_ID: 4 | PAYLOAD_LEN: 21
-> 09:43:44 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_LEAST_ONCE | TOPIC_NAME: devices/sample-device-01/messages/events/%24.sub=thermostat2 | PACKET_ID: 5 | PAYLOAD_LEN: 21

Nell'applicazione Azure IoT Central un operatore può:

  • Visualizzare i dati di telemetria inviati dai due componenti termostato nella pagina Panoramica :

    Screenshot che mostra la pagina di panoramica del dispositivo.

  • Visualizzare le proprietà del dispositivo nella pagina Informazioni. Questa pagina mostra le proprietà del componente informazioni sul dispositivo e i due componenti termostato:

    Screenshot che mostra la visualizzazione delle proprietà del dispositivo.

Personalizzare il modello di dispositivo

Gli sviluppatori di soluzioni possono personalizzare il modello di dispositivo creato automaticamente da IoT Central quando il dispositivo del controller di temperatura è connesso.

Per aggiungere una proprietà cloud per archiviare il nome del cliente associato al dispositivo:

  1. Nell'applicazione IoT Central passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Nel modello Temperature Controller selezionare +Aggiungi funzionalità.

  3. Immettere Nome cliente come nome visualizzato, selezionare Proprietà cloud come tipo di funzionalità, espandere la voce e scegliere String come schema. Quindi selezionare Salva.

Per personalizzare la modalità di visualizzazione dei comandi del report Get Max-Min nell'applicazione IoT Central:

  1. Passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Per getMaxMinReport (termostato1), sostituire Get Max-Min report.with Get thermostat1 status report.For get thermostatMinReport (thermostat1), replace Get Max-Min report.

  3. Per getMaxMinReport (termostato2), sostituire Get Max-Min report. With Get thermostat2 status report .For get thermostatMinReport (thermostat2), replace Get Max-Min report. with Get thermostat2 status report.

  4. Seleziona Salva.

Per personalizzare la visualizzazione delle proprietà scrivibili di Temperatura di destinazione nell'applicazione IoT Central:

  1. Passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Per targetTemperature (termostato1) sostituire Temperatura di destinazione con Temperatura di destinazione (1).For targetTemperature (thermostat1), replace Target Temperature with Target Temperature (1).

  3. Per targetTemperature (termostato2), sostituire Target Temperature con Target Temperature (2).

  4. Seleziona Salva.

I componenti termostato nel modello Temperature Controller includono la proprietà scrivibile Target Temperature , il modello di dispositivo include la proprietà cloud Customer Name . Creare una visualizzazione che può essere usata da un operatore per modificare queste proprietà:

  1. Selezionare Visualizzazioni e quindi il riquadro Modifica dei dati del dispositivo e del cloud.

  2. Immettere Properties come nome del modulo.

  3. Selezionare le proprietà Target Temperature (1), Target Temperature (2) e Customer Name (Nome cliente). Fare quindi clic su Aggiungi sezione.

  4. Salva le modifiche.

Screenshot che mostra una visualizzazione per l'aggiornamento dei valori delle proprietà.

Pubblicare il modello di dispositivo

Prima che un operatore possa visualizzare e usare le personalizzazioni apportate, è necessario pubblicare il modello di dispositivo.

Nel modello di dispositivo Termostato selezionare Pubblica. Nel pannello Pubblica questo modello di dispositivo nell'applicazione selezionare Pubblica.

Un operatore può ora usare la visualizzazione Proprietà per aggiornare i valori delle proprietà e chiamare i comandi denominati Get thermostat1 status report (Ottieni report sullo stato del termostato1) e Get thermostat2 status report (Ottieni report sullo stato del termostato2) nella pagina dei comandi del dispositivo:

  • Aggiornare i valori delle proprietà scrivibili nella pagina Proprietà :

    Screenshot che mostra l'aggiornamento delle proprietà del dispositivo.

  • Chiamare i comandi dalla pagina Comandi . Se si esegue il comando report di stato, selezionare una data e un'ora per il parametro Since prima di eseguirlo:

    Screenshot che mostra la chiamata a un comando.

    Screenshot che mostra una risposta al comando.

È possibile visualizzare il modo in cui il dispositivo risponde ai comandi e agli aggiornamenti delle proprietà:

<- 09:49:03 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $iothub/methods/POST/thermostat1*getMaxMinReport/?$rid=1 | PAYLOAD_LEN: 26
Info: Received PnP command for component=thermostat1, command=getMaxMinReport
Info: Returning success from command request for component=thermostat1
-> 09:49:03 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $iothub/methods/res/200/?$rid=1 | PAYLOAD_LEN: 117

...

<- 09:50:04 PUBLISH | IS_DUP: false | RETAIN: 0 | QOS: DELIVER_AT_MOST_ONCE | TOPIC_NAME: $iothub/twin/PATCH/properties/desired/?$version=2 | PAYLOAD_LEN: 63
Info: Received targetTemperature=67.000000 for component=thermostat2
Info: Sending acknowledgement of property to IoTHub for component=thermostat2

Esplora il codice

Prerequisiti

Per seguire la procedura descritta in questo articolo, sono necessarie le risorse seguenti:

Esaminare il codice

Nella copia del repository Microsoft Azure IoT SDK per C# scaricato in precedenza aprire il file della soluzione azure-iot-sdk-csharp-main\azureiot.sln in Visual Studio. In Esplora soluzioni espandere la cartella PnpDeviceSamples > TemperatureController e aprire i file Program.cs e TemperatureControllerSample.cs per visualizzare il codice per questo esempio.

L'esempio implementa il modello di linguaggio di definizione di Temperature Controller digital twin a più componenti.

Quando si esegue l'esempio per connettersi a IoT Central, il dispositivo viene registrato tramite il servizio Device Provisioning e viene generata una stringa di connessione. L'esempio recupera le informazioni necessarie sulla connessione del servizio Device Provisioning dall'ambiente.

In Program.cs il metodo Main chiama SetupDeviceClientAsync per:

  • Usare l'ID modello dtmi:com:example:TemperatureController;2 quando esegue il provisioning del dispositivo con DPS. IoT Central usa l'ID modello per identificare o generare il modello di dispositivo per questo dispositivo. Per altre informazioni, vedere Assegnare un dispositivo a un modello di dispositivo.
  • Creare una istanza DeviceClient per connettersi a IoT Central.
private static async Task<DeviceClient> SetupDeviceClientAsync(Parameters parameters, CancellationToken cancellationToken)
{
  DeviceClient deviceClient;
  switch (parameters.DeviceSecurityType.ToLowerInvariant())
  {
    case "dps":
      DeviceRegistrationResult dpsRegistrationResult = await ProvisionDeviceAsync(parameters, cancellationToken);
      var authMethod = new DeviceAuthenticationWithRegistrySymmetricKey(dpsRegistrationResult.DeviceId, parameters.DeviceSymmetricKey);
      deviceClient = InitializeDeviceClient(dpsRegistrationResult.AssignedHub, authMethod);
      break;

    case "connectionstring":
      // ...

    default:
      // ...
  }
  return deviceClient;
}

Il metodo main crea quindi un'istanza TemperatureControllerSample e chiama il PerformOperationsAsync metodo per gestire le interazioni con IoT Central.

In TemperatureControllerSample.cs il PerformOperationsAsync metodo :

  • Imposta un gestore per il comando di riavvio nel componente predefinito.
  • Imposta i gestori per i comandi getMaxMinReport nei due componenti termostato.
  • Imposta i gestori per ricevere gli aggiornamenti delle proprietà della temperatura di destinazione sui due componenti termostato.
  • Invia gli aggiornamenti iniziali delle proprietà delle informazioni sul dispositivo.
  • Invia periodicamente dati di telemetria sulla temperatura dai due componenti termostato.
  • Invia periodicamente dati di telemetria del working set dal componente predefinito.
  • Invia la temperatura massima dall'ultimo riavvio ogni volta che viene raggiunta una nuova temperatura massima nei due componenti termostato.
public async Task PerformOperationsAsync(CancellationToken cancellationToken)
{
  await _deviceClient.SetMethodHandlerAsync("reboot", HandleRebootCommandAsync, _deviceClient, cancellationToken);

  // For a component-level command, the command name is in the format "<component-name>*<command-name>".
  await _deviceClient.SetMethodHandlerAsync("thermostat1*getMaxMinReport", HandleMaxMinReportCommand, Thermostat1, cancellationToken);
  await _deviceClient.SetMethodHandlerAsync("thermostat2*getMaxMinReport", HandleMaxMinReportCommand, Thermostat2, cancellationToken);

  await _deviceClient.SetDesiredPropertyUpdateCallbackAsync(SetDesiredPropertyUpdateCallback, null, cancellationToken);
  _desiredPropertyUpdateCallbacks.Add(Thermostat1, TargetTemperatureUpdateCallbackAsync);
  _desiredPropertyUpdateCallbacks.Add(Thermostat2, TargetTemperatureUpdateCallbackAsync);

  await UpdateDeviceInformationAsync(cancellationToken);
  await SendDeviceSerialNumberAsync(cancellationToken);

  bool temperatureReset = true;
  _maxTemp[Thermostat1] = 0d;
  _maxTemp[Thermostat2] = 0d;

  while (!cancellationToken.IsCancellationRequested)
  {
    if (temperatureReset)
    {
      // Generate a random value between 5.0°C and 45.0°C for the current temperature reading for each "Thermostat" component.
      _temperature[Thermostat1] = Math.Round(s_random.NextDouble() * 40.0 + 5.0, 1);
      _temperature[Thermostat2] = Math.Round(s_random.NextDouble() * 40.0 + 5.0, 1);
    }

    await SendTemperatureAsync(Thermostat1, cancellationToken);
    await SendTemperatureAsync(Thermostat2, cancellationToken);
    await SendDeviceMemoryAsync(cancellationToken);

    temperatureReset = _temperature[Thermostat1] == 0 && _temperature[Thermostat2] == 0;
    await Task.Delay(5 * 1000);
  }
}

Il SendTemperatureAsync metodo mostra come il dispositivo invia i dati di telemetria relativi alla temperatura da un componente a IoT Central. Il SendTemperatureTelemetryAsync metodo usa la PnpConvention classe per compilare il messaggio:

private async Task SendTemperatureAsync(string componentName, CancellationToken cancellationToken)
{
  await SendTemperatureTelemetryAsync(componentName, cancellationToken);

  double maxTemp = _temperatureReadingsDateTimeOffset[componentName].Values.Max<double>();
  if (maxTemp > _maxTemp[componentName])
  {
    _maxTemp[componentName] = maxTemp;
    await UpdateMaxTemperatureSinceLastRebootAsync(componentName, cancellationToken);
  }
}

private async Task SendTemperatureTelemetryAsync(string componentName, CancellationToken cancellationToken)
{
  const string telemetryName = "temperature";
  double currentTemperature = _temperature[componentName];
  using Message msg = PnpConvention.CreateMessage(telemetryName, currentTemperature, componentName);

  await _deviceClient.SendEventAsync(msg, cancellationToken);

  if (_temperatureReadingsDateTimeOffset.ContainsKey(componentName))
  {
    _temperatureReadingsDateTimeOffset[componentName].TryAdd(DateTimeOffset.UtcNow, currentTemperature);
  }
  else
  {
    _temperatureReadingsDateTimeOffset.TryAdd(
      componentName,
      new Dictionary<DateTimeOffset, double>
      {
        { DateTimeOffset.UtcNow, currentTemperature },
      });
  }
}

Il UpdateMaxTemperatureSinceLastRebootAsync metodo invia un maxTempSinceLastReboot aggiornamento della proprietà a IoT Central. Questo metodo usa la PnpConvention classe per creare la patch:

private async Task UpdateMaxTemperatureSinceLastRebootAsync(string componentName, CancellationToken cancellationToken)
{
  const string propertyName = "maxTempSinceLastReboot";
  double maxTemp = _maxTemp[componentName];
  TwinCollection reportedProperties = PnpConvention.CreateComponentPropertyPatch(componentName, propertyName, maxTemp);

  await _deviceClient.UpdateReportedPropertiesAsync(reportedProperties, cancellationToken);
}

Il TargetTemperatureUpdateCallbackAsync metodo gestisce l'aggiornamento della proprietà della temperatura di destinazione scrivibile da IoT Central. Questo metodo usa la PnpConvention classe per leggere il messaggio di aggiornamento della proprietà e costruire la risposta:

private async Task TargetTemperatureUpdateCallbackAsync(TwinCollection desiredProperties, object userContext)
{
  const string propertyName = "targetTemperature";
  string componentName = (string)userContext;

  bool targetTempUpdateReceived = PnpConvention.TryGetPropertyFromTwin(
    desiredProperties,
    propertyName,
    out double targetTemperature,
    componentName);
  if (!targetTempUpdateReceived)
  {
      return;
  }

  TwinCollection pendingReportedProperty = PnpConvention.CreateComponentWritablePropertyResponse(
      componentName,
      propertyName,
      targetTemperature,
      (int)StatusCode.InProgress,
      desiredProperties.Version);

  await _deviceClient.UpdateReportedPropertiesAsync(pendingReportedProperty);

  // Update Temperature in 2 steps
  double step = (targetTemperature - _temperature[componentName]) / 2d;
  for (int i = 1; i <= 2; i++)
  {
      _temperature[componentName] = Math.Round(_temperature[componentName] + step, 1);
      await Task.Delay(6 * 1000);
  }

  TwinCollection completedReportedProperty = PnpConvention.CreateComponentWritablePropertyResponse(
      componentName,
      propertyName,
      _temperature[componentName],
      (int)StatusCode.Completed,
      desiredProperties.Version,
      "Successfully updated target temperature");

  await _deviceClient.UpdateReportedPropertiesAsync(completedReportedProperty);
}

Il HandleMaxMinReportCommand metodo gestisce i comandi per i componenti chiamati da IoT Central:

private Task<MethodResponse> HandleMaxMinReportCommand(MethodRequest request, object userContext)
{
    try
    {
        string componentName = (string)userContext;
        DateTime sinceInUtc = JsonConvert.DeserializeObject<DateTime>(request.DataAsJson);
        var sinceInDateTimeOffset = new DateTimeOffset(sinceInUtc);

        if (_temperatureReadingsDateTimeOffset.ContainsKey(componentName))
        {

            Dictionary<DateTimeOffset, double> allReadings = _temperatureReadingsDateTimeOffset[componentName];
            Dictionary<DateTimeOffset, double> filteredReadings = allReadings.Where(i => i.Key > sinceInDateTimeOffset)
                .ToDictionary(i => i.Key, i => i.Value);

            if (filteredReadings != null && filteredReadings.Any())
            {
                var report = new
                {
                    maxTemp = filteredReadings.Values.Max<double>(),
                    minTemp = filteredReadings.Values.Min<double>(),
                    avgTemp = filteredReadings.Values.Average(),
                    startTime = filteredReadings.Keys.Min(),
                    endTime = filteredReadings.Keys.Max(),
                };

                byte[] responsePayload = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(report));
                return Task.FromResult(new MethodResponse(responsePayload, (int)StatusCode.Completed));
            }

            return Task.FromResult(new MethodResponse((int)StatusCode.NotFound));
        }

        return Task.FromResult(new MethodResponse((int)StatusCode.NotFound));
    }
    catch (JsonReaderException ex)
    {
        // ...
    }
}

Ottenere informazioni di connessione

Quando si eseguirà l'applicazione del dispositivo di esempio più avanti in questa esercitazione, saranno necessari i valori di configurazione seguenti:

  • Ambito ID: nell'applicazione IoT Central passare a Autorizzazioni > Gruppi di connessioni del dispositivo. Prendere nota del valore dell'Ambito ID.
  • Raggruppare la chiave primaria: nell'applicazione IoT Central passare a Autorizzazioni Gruppi > di connessioni > del dispositivo SAS-IoT-Devices. Prendere nota del valore Chiave primaria della firma di accesso condivisa.

Usare Azure Cloud Shell per generare una chiave del dispositivo dalla chiave primaria del gruppo recuperata:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Prendere nota della chiave dispositivo generata. Questo valore verrà usato in un passaggio successivo dell'esercitazione.

Nota

Per eseguire questo esempio, non è necessario registrare il dispositivo in anticipo nell'applicazione IoT Central. L'esempio usa la funzionalità IoT Central per registrare automaticamente i dispositivi quando si connettono per la prima volta.

Eseguire il codice

Nota

Configurare TemperatureController come progetto di avvio prima di eseguire il codice.

Per eseguire l'applicazione di esempio in Visual Studio:

  1. In Esplora soluzioni selezionare il file di progetto PnpDeviceSamples > TemperatureController.

  2. Passare a Project TemperatureController Properties Debug (Debug delle proprietà > di Project > TemperatureController). Aggiungere quindi al progetto le variabili di ambiente seguenti:

    Nome Valore
    IOTHUB_DEVICE_SECURITY_TYPE DPS
    IOTHUB_DEVICE_DPS_ENDPOINT global.azure-devices-provisioning.net
    IOTHUB_DEVICE_DPS_ID_SCOPE Il valore di ambito ID annotato in precedenza.
    IOTHUB_DEVICE_DPS_DEVICE_ID dispositivo-esempio-01
    IOTHUB_DEVICE_DPS_DEVICE_KEY Il valore di chiave dispositivo generato e annotato in precedenza.

È ora possibile eseguire l'esempio ed eseguirne il debug in Visual Studio.

L'output seguente mostra la registrazione del dispositivo e la connessione a IoT Central. L'esempio inizia a inviare dati di telemetria:

[03/31/2021 14:43:17]info: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Press Control+C to quit the sample.
[03/31/2021 14:43:17]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Set up the device client.
[03/31/2021 14:43:18]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Initializing via DPS
[03/31/2021 14:43:27]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Set handler for 'reboot' command.
[03/31/2021 14:43:27]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Connection status change registered - status=Connected, reason=Connection_Ok.
[03/31/2021 14:43:28]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Set handler for "getMaxMinReport" command.
[03/31/2021 14:43:28]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Set handler to receive 'targetTemperature' updates.
[03/31/2021 14:43:28]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component = 'deviceInformation', properties update is complete.
[03/31/2021 14:43:28]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - { "serialNumber": "SR-123456" } is complete.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Telemetry: Sent - component="thermostat1", { "temperature": 34.2 } in °C.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component="thermostat1", { "maxTempSinceLastReboot": 34.2 } in °C is complete.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Telemetry: Sent - component="thermostat2", { "temperature": 25.1 } in °C.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component="thermostat2", { "maxTempSinceLastReboot": 25.1 } in °C is complete.
[03/31/2021 14:43:29]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Telemetry: Sent - {"workingSet":31412} in KB.

Nell'applicazione Azure IoT Central un operatore può:

  • Visualizzare i dati di telemetria inviati dai due componenti termostato nella pagina Panoramica :

    Screenshot che mostra la pagina di panoramica del dispositivo.

  • Visualizzare le proprietà del dispositivo nella pagina Informazioni. Questa pagina mostra le proprietà del componente informazioni sul dispositivo e i due componenti termostato:

    Screenshot che mostra la visualizzazione delle proprietà del dispositivo.

Personalizzare il modello di dispositivo

Gli sviluppatori di soluzioni possono personalizzare il modello di dispositivo creato automaticamente da IoT Central quando il dispositivo del controller di temperatura è connesso.

Per aggiungere una proprietà cloud per archiviare il nome del cliente associato al dispositivo:

  1. Nell'applicazione IoT Central passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Nel modello Temperature Controller selezionare +Aggiungi funzionalità.

  3. Immettere Nome cliente come nome visualizzato, selezionare Proprietà cloud come tipo di funzionalità, espandere la voce e scegliere String come schema. Quindi selezionare Salva.

Per personalizzare la modalità di visualizzazione dei comandi del report Get Max-Min nell'applicazione IoT Central:

  1. Passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Per getMaxMinReport (termostato1), sostituire Get Max-Min report.with Get thermostat1 status report.For get thermostatMinReport (thermostat1), replace Get Max-Min report.

  3. Per getMaxMinReport (termostato2), sostituire Get Max-Min report. With Get thermostat2 status report .For get thermostatMinReport (thermostat2), replace Get Max-Min report. with Get thermostat2 status report.

  4. Seleziona Salva.

Per personalizzare la visualizzazione delle proprietà scrivibili di Temperatura di destinazione nell'applicazione IoT Central:

  1. Passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Per targetTemperature (termostato1) sostituire Temperatura di destinazione con Temperatura di destinazione (1).For targetTemperature (thermostat1), replace Target Temperature with Target Temperature (1).

  3. Per targetTemperature (termostato2), sostituire Target Temperature con Target Temperature (2).

  4. Seleziona Salva.

I componenti termostato nel modello Temperature Controller includono la proprietà scrivibile Target Temperature , il modello di dispositivo include la proprietà cloud Customer Name . Creare una visualizzazione che può essere usata da un operatore per modificare queste proprietà:

  1. Selezionare Visualizzazioni e quindi il riquadro Modifica dei dati del dispositivo e del cloud.

  2. Immettere Properties come nome del modulo.

  3. Selezionare le proprietà Target Temperature (1), Target Temperature (2) e Customer Name (Nome cliente). Fare quindi clic su Aggiungi sezione.

  4. Salva le modifiche.

Screenshot che mostra una visualizzazione per l'aggiornamento dei valori delle proprietà.

Pubblicare il modello di dispositivo

Prima che un operatore possa visualizzare e usare le personalizzazioni apportate, è necessario pubblicare il modello di dispositivo.

Nel modello di dispositivo Termostato selezionare Pubblica. Nel pannello Pubblica questo modello di dispositivo nell'applicazione selezionare Pubblica.

Un operatore può ora usare la visualizzazione Proprietà per aggiornare i valori delle proprietà e chiamare i comandi denominati Get thermostat1 status report (Ottieni report sullo stato del termostato1) e Get thermostat2 status report (Ottieni report sullo stato del termostato2) nella pagina dei comandi del dispositivo:

  • Aggiornare i valori delle proprietà scrivibili nella pagina Proprietà :

    Screenshot che mostra l'aggiornamento delle proprietà del dispositivo.

  • Chiamare i comandi dalla pagina Comandi . Se si esegue il comando report di stato, selezionare una data e un'ora per il parametro Since prima di eseguirlo:

    Screenshot che mostra la chiamata a un comando.

    Screenshot che mostra una risposta al comando.

È possibile visualizzare il modo in cui il dispositivo risponde ai comandi e agli aggiornamenti delle proprietà:

[03/31/2021 14:47:00]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Command: Received - component="thermostat2", generating max, min and avg temperature report since 31/03/2021 06:00:00.
[03/31/2021 14:47:00]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Command: component="thermostat2", MaxMinReport since 31/03/2021 06:00:00: maxTemp=36.4, minTemp=36.4, avgTemp=36.4, startTime=31/03/2021 14:46:33, endTime=31/03/2021 14:46:55

...

[03/31/2021 14:46:36]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Received - component="thermostat1", { "targetTemperature": 67°C }.
[03/31/2021 14:46:36]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component="thermostat1", {"targetTemperature": 67 } in °C is InProgress.
[03/31/2021 14:46:49]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Property: Update - component="thermostat1", {"targetTemperature": 67 } in °C is Completed
[03/31/2021 14:46:49]dbug: Microsoft.Azure.Devices.Client.Samples.TemperatureControllerSample[0]
      Telemetry: Sent - component="thermostat1", { "temperature": 67 } in °C.

Esplora il codice

Prerequisiti

Per seguire la procedura descritta in questo articolo, sono necessarie le risorse seguenti:

Esaminare il codice

Nella copia di Microsoft Azure IoT SDK per Java scaricata in precedenza aprire il file azure-iot-sdk-java/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/device/TemperatureController.java in un editor di testo.

L'esempio implementa il modello di linguaggio di definizione di Temperature Controller digital twin a più componenti.

Quando si esegue l'esempio per connettersi a IoT Central, il dispositivo viene registrato tramite il servizio Device Provisioning e viene generata una stringa di connessione. L'esempio recupera le informazioni necessarie sulla connessione del servizio Device Provisioning dall'ambiente di riga di comando.

Il metodo main:

  • Chiama initializeAndProvisionDevice per impostare l'ID del modello dtmi:com:example:TemperatureController;2, usare DPS per eseguire il provisioning e registrare il dispositivo, creare un'istanza DeviceClient e connettersi all'applicazione IoT Central. IoT Central usa l'ID modello per identificare o generare il modello di dispositivo per questo dispositivo. Per altre informazioni, vedere Assegnare un dispositivo a un modello di dispositivo.
  • Crea gestori di comandi per i getMaxMinReport comandi e reboot .
  • Crea gestori di aggiornamento delle proprietà per le proprietà scrivibili targetTemperature .
  • Invia i valori iniziali per le proprietà nell'interfaccia Informazioni dispositivo e le proprietà Memoria dispositivo e Numero di serie.
  • Avvia un thread per inviare dati di telemetria relativi alla temperatura dai due termostati e aggiornare la maxTempSinceLastReboot proprietà ogni cinque secondi.
public static void main(String[] args) throws Exception {

  // ...
  
  switch (deviceSecurityType.toLowerCase())
  {
    case "dps":
    {
      if (validateArgsForDpsFlow())
      {
        initializeAndProvisionDevice();
        break;
      }
      throw new IllegalArgumentException("Required environment variables are not set for DPS flow, please recheck your environment.");
    }
    case "connectionstring":
    {
      // ...
    }
    default:
    {
      // ...
    }
  }
  
  deviceClient.subscribeToMethods(new MethodCallback(), null);
  
  deviceClient.subscribeToDesiredPropertiesAsync(
  {
  (twin, context) ->
      TwinCollection desiredProperties = twin.getDesiredProperties();
      for (String desiredPropertyKey : desiredProperties.keySet())
      {
          TargetTemperatureUpdateCallback.onPropertyChanged(new Property(desiredPropertyKey, desiredProperties.get(desiredPropertyKey)), null);
      }
  },
  null,
  (exception, context) ->
  {
      if (exception == null)
      {
          log.info("Successfully subscribed to desired properties. Getting initial state");
          deviceClient.getTwinAsync(
              (twin, getTwinException, getTwinContext) ->
              {
                  log.info("Initial twin state received");
                  log.info(twin.toString());
              },
              null);
      }
      else
      {
          log.info("Failed to subscribe to desired properties. Error code {}", exception.getStatusCode());
          System.exit(-1);
      }
  },
  null);

  updateDeviceInformation();
  sendDeviceMemory();
  sendDeviceSerialNumber();
  
  final AtomicBoolean temperatureReset = new AtomicBoolean(true);
  maxTemperature.put(THERMOSTAT_1, 0.0d);
  maxTemperature.put(THERMOSTAT_2, 0.0d);
  
  new Thread(new Runnable() {
    @SneakyThrows({InterruptedException.class, IOException.class})
    @Override
    public void run() {
      while (true) {
        if (temperatureReset.get()) {
          // Generate a random value between 5.0°C and 45.0°C for the current temperature reading for each "Thermostat" component.
          temperature.put(THERMOSTAT_1, BigDecimal.valueOf(random.nextDouble() * 40 + 5).setScale(1, RoundingMode.HALF_UP).doubleValue());
          temperature.put(THERMOSTAT_2, BigDecimal.valueOf(random.nextDouble() * 40 + 5).setScale(1, RoundingMode.HALF_UP).doubleValue());
        }

        sendTemperatureReading(THERMOSTAT_1);
        sendTemperatureReading(THERMOSTAT_2);

        temperatureReset.set(temperature.get(THERMOSTAT_1) == 0 && temperature.get(THERMOSTAT_2) == 0);
        Thread.sleep(5 * 1000);
      }
    }
  }).start();
}

Il metodo initializeAndProvisionDevice mostra in che modo il dispositivo usa DPS per registrarsi e connettersi a IoT Central. Il payload include l'ID modello usato da IoT Central per assegnare un dispositivo a un modello di dispositivo:

private static void initializeAndProvisionDevice() throws Exception {
  SecurityProviderSymmetricKey securityClientSymmetricKey = new SecurityProviderSymmetricKey(deviceSymmetricKey.getBytes(), registrationId);
  ProvisioningDeviceClient provisioningDeviceClient;
  ProvisioningStatus provisioningStatus = new ProvisioningStatus();

  provisioningDeviceClient = ProvisioningDeviceClient.create(globalEndpoint, scopeId, provisioningProtocol, securityClientSymmetricKey);

  AdditionalData additionalData = new AdditionalData();
  additionalData.setProvisioningPayload(com.microsoft.azure.sdk.iot.provisioning.device.plugandplay.PnpHelper.createDpsPayload(MODEL_ID));

  ProvisioningDeviceClientRegistrationResult registrationResult = provisioningDeviceClient.registerDeviceSync(additionalData);

    ClientOptions options = ClientOptions.builder().modelId(MODEL_ID).build();
    if (registrationResult.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED) {
        System.out.println("IotHUb Uri : " + registrationResult.getIothubUri());
        System.out.println("Device ID : " + registrationResult.getDeviceId());
        String iotHubUri = registrationResult.getIothubUri();
        String deviceId = registrationResult.getDeviceId();
        log.debug("Opening the device client.");
        deviceClient = new DeviceClient(iotHubUri, deviceId, securityClientSymmetricKey, IotHubClientProtocol.MQTT, options);
        deviceClient.open(true);
    }
}

Il sendTemperatureTelemetry metodo mostra come il dispositivo invia i dati di telemetria relativi alla temperatura da un componente a IoT Central. Questo metodo usa la PnpConvention classe per creare il messaggio:

  private static void sendTemperatureTelemetry(String componentName) {
    String telemetryName = "temperature";
    double currentTemperature = temperature.get(componentName);

    Message message = PnpConvention.createIotHubMessageUtf8(telemetryName, currentTemperature, componentName);
    deviceClient.sendEventAsync(message, new MessageIotHubEventCallback(), message);

    // Add the current temperature entry to the list of temperature readings.
    Map<Date, Double> currentReadings;
    if (temperatureReadings.containsKey(componentName)) {
      currentReadings = temperatureReadings.get(componentName);
    } else {
      currentReadings = new HashMap<>();
    }
    currentReadings.put(new Date(), currentTemperature);
    temperatureReadings.put(componentName, currentReadings);
  }

Il updateMaxTemperatureSinceLastReboot metodo invia un maxTempSinceLastReboot aggiornamento di proprietà da un componente a IoT Central. Questo metodo usa la PnpConvention classe per creare la patch:

private static void updateMaxTemperatureSinceLastReboot(String componentName) throws IOException {
  String propertyName = "maxTempSinceLastReboot";
  double maxTemp = maxTemperature.get(componentName);

  TwinCollection reportedProperty = PnpConvention.createComponentPropertyPatch(propertyName, maxTemp, componentName);
  deviceClient.updateReportedPropertiesAsync(reportedProperty, sendReportedPropertiesResponseCallback, null);
  log.debug("Property: Update - {\"{}\": {}°C} is {}.", propertyName, maxTemp, StatusCode.COMPLETED);
}

La TargetTemperatureUpdateCallback classe contiene il onPropertyChanged metodo per gestire gli aggiornamenti delle proprietà scrivibili a un componente da IoT Central. Questo metodo usa la PnpConvention classe per creare la risposta:

private static class TargetTemperatureUpdateCallback
{
    final static String propertyName = "targetTemperature";
    @SneakyThrows(InterruptedException.class)
    public static void onPropertyChanged(Property property, Object context) {
        String componentName = (String) context;
        if (property.getKey().equalsIgnoreCase(componentName)) {
            double targetTemperature = (double) ((TwinCollection) property.getValue()).get(propertyName);
            log.debug("Property: Received - component=\"{}\", {\"{}\": {}°C}.", componentName, propertyName, targetTemperature);
            TwinCollection pendingPropertyPatch = PnpConvention.createComponentWritablePropertyResponse(
                    propertyName,
                    targetTemperature,
                    componentName,
                    StatusCode.IN_PROGRESS.value,
                    property.getVersion().longValue(),
                    null);
            deviceClient.updateReportedPropertiesAsync(pendingPropertyPatch, sendReportedPropertiesResponseCallback, null);
            log.debug("Property: Update - component=\"{}\", {\"{}\": {}°C} is {}", componentName, propertyName, targetTemperature, StatusCode.IN_PROGRESS);
            // Update temperature in 2 steps
            double step = (targetTemperature - temperature.get(componentName)) / 2;
            for (int i = 1; i <=2; i++) {
                temperature.put(componentName, BigDecimal.valueOf(temperature.get(componentName) + step).setScale(1, RoundingMode.HALF_UP).doubleValue());
                Thread.sleep(5 * 1000);
            }
            TwinCollection completedPropertyPatch = PnpConvention.createComponentWritablePropertyResponse(
                    propertyName,
                    temperature.get(componentName),
                    componentName,
                    StatusCode.COMPLETED.value,
                    property.getVersion().longValue(),
                    "Successfully updated target temperature.");
            deviceClient.updateReportedPropertiesAsync(completedPropertyPatch, sendReportedPropertiesResponseCallback, null);
            log.debug("Property: Update - {\"{}\": {}°C} is {}", propertyName, temperature.get(componentName), StatusCode.COMPLETED);
        } else {
            log.debug("Property: Received an unrecognized property update from service.");
        }
    }
}

La MethodCallback classe contiene il onMethodInvoked metodo per gestire i comandi dei componenti chiamati da IoT Central:

private static class MethodCallback implements com.microsoft.azure.sdk.iot.device.twin.MethodCallback
{
    final String reboot = "reboot";
    final String getMaxMinReport1 = "thermostat1*getMaxMinReport";
    final String getMaxMinReport2 = "thermostat2*getMaxMinReport";
    @SneakyThrows(InterruptedException.class)
    @Override
    public DirectMethodResponse onMethodInvoked(String methodName, DirectMethodPayload methodData, Object context) {
        String jsonRequest = methodData.getPayload(String.class);
        switch (methodName) {
            case reboot:
                int delay = getCommandRequestValue(jsonRequest, Integer.class);
                log.debug("Command: Received - Rebooting thermostat (resetting temperature reading to 0°C after {} seconds).", delay);
                Thread.sleep(delay * 1000L);
                temperature.put(THERMOSTAT_1, 0.0d);
                temperature.put(THERMOSTAT_2, 0.0d);
                maxTemperature.put(THERMOSTAT_1, 0.0d);
                maxTemperature.put(THERMOSTAT_2, 0.0d);
                temperatureReadings.clear();
                return new DirectMethodResponse(StatusCode.COMPLETED.value, null);
            case getMaxMinReport1:
            case getMaxMinReport2:
                String[] words = methodName.split("\\*");
                String componentName = words[0];
                if (temperatureReadings.containsKey(componentName)) {
                    Date since = getCommandRequestValue(jsonRequest, Date.class);
                    log.debug("Command: Received - component=\"{}\", generating min, max, avg temperature report since {}", componentName, since);
                    Map<Date, Double> allReadings = temperatureReadings.get(componentName);
                    Map<Date, Double> filteredReadings = allReadings.entrySet().stream()
                            .filter(map -> map.getKey().after(since))
                            .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
                    if (!filteredReadings.isEmpty()) {
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                        double maxTemp = Collections.max(filteredReadings.values());
                        double minTemp = Collections.min(filteredReadings.values());
                        double avgTemp = filteredReadings.values().stream().mapToDouble(Double::doubleValue).average().orElse(Double.NaN);
                        String startTime =  sdf.format(Collections.min(filteredReadings.keySet()));
                        String endTime =  sdf.format(Collections.max(filteredReadings.keySet()));
                        String responsePayload = String.format(
                                "{\"maxTemp\": %.1f, \"minTemp\": %.1f, \"avgTemp\": %.1f, \"startTime\": \"%s\", \"endTime\": \"%s\"}",
                                maxTemp,
                                minTemp,
                                avgTemp,
                                startTime,
                                endTime);
                        log.debug("Command: MaxMinReport since {}: \"maxTemp\": {}°C, \"minTemp\": {}°C, \"avgTemp\": {}°C, \"startTime\": {}, \"endTime\": {}",
                                since,
                                maxTemp,
                                minTemp,
                                avgTemp,
                                startTime,
                                endTime);
                        return new DirectMethodResponse(StatusCode.COMPLETED.value, responsePayload);
                    }
                    log.debug("Command: component=\"{}\", no relevant readings found since {}, cannot generate any report.", componentName, since);
                    return new DirectMethodResponse(StatusCode.NOT_FOUND.value, null);
                }
                log.debug("Command: component=\"{}\", no temperature readings sent yet, cannot generate any report.", componentName);
                return new DirectMethodResponse(StatusCode.NOT_FOUND.value, null);
            default:
                log.debug("Command: command=\"{}\" is not implemented, no action taken.", methodName);
                return new DirectMethodResponse(StatusCode.NOT_FOUND.value, null);
        }
    }
}

Ottenere informazioni di connessione

Quando si eseguirà l'applicazione del dispositivo di esempio più avanti in questa esercitazione, saranno necessari i valori di configurazione seguenti:

  • Ambito ID: nell'applicazione IoT Central passare a Autorizzazioni > Gruppi di connessioni del dispositivo. Prendere nota del valore dell'Ambito ID.
  • Raggruppare la chiave primaria: nell'applicazione IoT Central passare a Autorizzazioni Gruppi > di connessioni > del dispositivo SAS-IoT-Devices. Prendere nota del valore Chiave primaria della firma di accesso condivisa.

Usare Azure Cloud Shell per generare una chiave del dispositivo dalla chiave primaria del gruppo recuperata:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Prendere nota della chiave dispositivo generata. Questo valore verrà usato in un passaggio successivo dell'esercitazione.

Nota

Per eseguire questo esempio, non è necessario registrare il dispositivo in anticipo nell'applicazione IoT Central. L'esempio usa la funzionalità IoT Central per registrare automaticamente i dispositivi quando si connettono per la prima volta.

In Windows passare alla cartella radice del repository Azure IoT SDK per Java scaricato.

Eseguire il comando seguente per compilare l'applicazione di esempio:

mvn install -T 2C -DskipTests

Eseguire il codice

Per eseguire l'applicazione di esempio, aprire un ambiente della riga di comando e passare alla cartella azure-iot-sdk-java/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample che contiene la cartella src con il file di esempio TemperatureController.java.

Impostare le variabili di ambiente per configurare l'esempio. Il frammento di codice seguente mostra come impostare le variabili di ambiente al prompt dei comandi di Windows. Se si usa una shell bash, sostituire i comandi set con i comandi export:

set IOTHUB_DEVICE_SECURITY_TYPE=DPS
set IOTHUB_DEVICE_DPS_ID_SCOPE=<The ID scope you made a note of previously>
set IOTHUB_DEVICE_DPS_DEVICE_ID=sample-device-01
set IOTHUB_DEVICE_DPS_DEVICE_KEY=<The generated device key you made a note of previously>
set IOTHUB_DEVICE_DPS_ENDPOINT=global.azure-devices-provisioning.net

Eseguire l'esempio:

mvn exec:java -Dexec.mainClass="samples.com.microsoft.azure.sdk.iot.device.TemperatureController"

L'output seguente mostra la registrazione del dispositivo e la connessione a IoT Central. L'esempio inizia a inviare dati di telemetria:

2021-03-30 15:33:25.138 DEBUG TemperatureController:123 - Initialize the device client.
Waiting for Provisioning Service to register
Waiting for Provisioning Service to register
IotHUb Uri : iotc-60a.....azure-devices.net
Device ID : sample-device-01
2021-03-30 15:33:38.294 DEBUG TemperatureController:247 - Opening the device client.
2021-03-30 15:33:38.307 INFO  ExponentialBackoffWithJitter:98 - NOTE: A new instance of ExponentialBackoffWithJitter has been created with the following properties. Retry Count: 2147483647, Min Backoff Interval: 100, Max Backoff Interval: 10000, Max Time Between Retries: 100, Fast Retry Enabled: true
2021-03-30 15:33:38.321 INFO  ExponentialBackoffWithJitter:98 - NOTE: A new instance of ExponentialBackoffWithJitter has been created with the following properties. Retry Count: 2147483647, Min Backoff Interval: 100, Max Backoff Interval: 10000, Max Time Between Retries: 100, Fast Retry Enabled: true
2021-03-30 15:33:38.427 DEBUG MqttIotHubConnection:274 - Opening MQTT connection...
2021-03-30 15:33:38.427 DEBUG Mqtt:123 - Sending MQTT CONNECT packet...
2021-03-30 15:33:44.628 DEBUG Mqtt:126 - Sent MQTT CONNECT packet was acknowledged
2021-03-30 15:33:44.630 DEBUG Mqtt:256 - Sending MQTT SUBSCRIBE packet for topic devices/sample-device-01/messages/devicebound/#
2021-03-30 15:33:44.731 DEBUG Mqtt:261 - Sent MQTT SUBSCRIBE packet for topic devices/sample-device-01/messages/devicebound/# was acknowledged
2021-03-30 15:33:44.733 DEBUG MqttIotHubConnection:279 - MQTT connection opened successfully
2021-03-30 15:33:44.733 DEBUG IotHubTransport:302 - The connection to the IoT Hub has been established
2021-03-30 15:33:44.734 INFO  IotHubTransport:1429 - Updating transport status to new status CONNECTED with reason CONNECTION_OK
2021-03-30 15:33:44.735 DEBUG IotHubTransport:1439 - Invoking connection status callbacks with new status details
2021-03-30 15:33:44.739 DEBUG IotHubTransport:394 - Client connection opened successfully
2021-03-30 15:33:44.740 INFO  DeviceClient:438 - Device client opened successfully
2021-03-30 15:33:44.740 DEBUG TemperatureController:152 - Set handler for "reboot" command.
2021-03-30 15:33:44.742 DEBUG TemperatureController:153 - Set handler for "getMaxMinReport" command.
2021-03-30 15:33:44.774 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [aaaa0000-bb11-2222-33cc-444444dddddd] Message Id [bbbb1111-cc22-3333-44dd-555555eeeeee] Device Operation Type [DEVICE_OPERATION_METHOD_SUBSCRIBE_REQUEST] )
2021-03-30 15:33:44.774 DEBUG TemperatureController:156 - Set handler to receive "targetTemperature" updates.
2021-03-30 15:33:44.775 INFO  IotHubTransport:1344 - Sending message ( Message details: Correlation Id [aaaa0000-bb11-2222-33cc-444444dddddd] Message Id [bbbb1111-cc22-3333-44dd-555555eeeeee] Device Operation Type [DEVICE_OPERATION_METHOD_SUBSCRIBE_REQUEST] )
2021-03-30 15:33:44.779 DEBUG Mqtt:256 - Sending MQTT SUBSCRIBE packet for topic $iothub/methods/POST/#
2021-03-30 15:33:44.793 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [cccc2222-dd33-4444-55ee-666666ffffff] Message Id [dddd3333-ee44-5555-66ff-777777aaaaaa] Device Operation Type [DEVICE_OPERATION_TWIN_SUBSCRIBE_DESIRED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.794 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [eeee4444-ff55-6666-77aa-888888bbbbbb] Message Id [ffff5555-aa66-7777-88bb-999999cccccc] Request Id [0] Device Operation Type [DEVICE_OPERATION_TWIN_GET_REQUEST] )
2021-03-30 15:33:44.819 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [aaaa6666-bb77-8888-99cc-000000dddddd] Message Id [aaaa0000-bb11-2222-33cc-444444dddddd] Device Operation Type [DEVICE_OPERATION_TWIN_SUBSCRIBE_DESIRED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.881 DEBUG Mqtt:261 - Sent MQTT SUBSCRIBE packet for topic $iothub/methods/POST/# was acknowledged
2021-03-30 15:33:44.882 INFO  IotHubTransport:1344 - Sending message ( Message details: Correlation Id [cccc2222-dd33-4444-55ee-666666ffffff] Message Id [dddd3333-ee44-5555-66ff-777777aaaaaa] Device Operation Type [DEVICE_OPERATION_TWIN_SUBSCRIBE_DESIRED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.882 DEBUG Mqtt:256 - Sending MQTT SUBSCRIBE packet for topic $iothub/twin/res/#
2021-03-30 15:33:44.893 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [bbbb1111-cc22-3333-44dd-555555eeeeee] Message Id [cccc2222-dd33-4444-55ee-666666ffffff] Request Id [1] Device Operation Type [DEVICE_OPERATION_TWIN_UPDATE_REPORTED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.904 DEBUG TemperatureController:423 - Property: Update - component = "deviceInformation" is COMPLETED.
2021-03-30 15:33:44.915 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [dddd3333-ee44-5555-66ff-777777aaaaaa] Message Id [eeee4444-ff55-6666-77aa-888888bbbbbb] )
2021-03-30 15:33:44.915 DEBUG TemperatureController:434 - Telemetry: Sent - {"workingSet": 1024.0KiB }
2021-03-30 15:33:44.915 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [ffff5555-aa66-7777-88bb-999999cccccc] Message Id [aaaa6666-bb77-8888-99cc-000000dddddd] Request Id [2] Device Operation Type [DEVICE_OPERATION_TWIN_UPDATE_REPORTED_PROPERTIES_REQUEST] )
2021-03-30 15:33:44.916 DEBUG TemperatureController:442 - Property: Update - {"serialNumber": SR-123456} is COMPLETED
2021-03-30 15:33:44.927 INFO  IotHubTransport:489 - Message was queued to be sent later ( Message details: Correlation Id [aaaa0000-bb11-2222-33cc-444444dddddd] Message Id [bbbb1111-cc22-3333-44dd-555555eeeeee] )
2021-03-30 15:33:44.927 DEBUG TemperatureController:461 - Telemetry: Sent - {"temperature": 5.8°C} with message Id bbbb1111-cc22-3333-44dd-555555eeeeee.

Nell'applicazione Azure IoT Central un operatore può:

  • Visualizzare i dati di telemetria inviati dai due componenti termostato nella pagina Panoramica :

    Screenshot che mostra la pagina di panoramica del dispositivo.

  • Visualizzare le proprietà del dispositivo nella pagina Informazioni. Questa pagina mostra le proprietà del componente informazioni sul dispositivo e i due componenti termostato:

    Screenshot che mostra la visualizzazione delle proprietà del dispositivo.

Personalizzare il modello di dispositivo

Gli sviluppatori di soluzioni possono personalizzare il modello di dispositivo creato automaticamente da IoT Central quando il dispositivo del controller di temperatura è connesso.

Per aggiungere una proprietà cloud per archiviare il nome del cliente associato al dispositivo:

  1. Nell'applicazione IoT Central passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Nel modello Temperature Controller selezionare +Aggiungi funzionalità.

  3. Immettere Nome cliente come nome visualizzato, selezionare Proprietà cloud come tipo di funzionalità, espandere la voce e scegliere String come schema. Quindi selezionare Salva.

Per personalizzare la modalità di visualizzazione dei comandi del report Get Max-Min nell'applicazione IoT Central:

  1. Passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Per getMaxMinReport (termostato1), sostituire Get Max-Min report.with Get thermostat1 status report.For get thermostatMinReport (thermostat1), replace Get Max-Min report.

  3. Per getMaxMinReport (termostato2), sostituire Get Max-Min report. With Get thermostat2 status report .For get thermostatMinReport (thermostat2), replace Get Max-Min report. with Get thermostat2 status report.

  4. Seleziona Salva.

Per personalizzare la visualizzazione delle proprietà scrivibili di Temperatura di destinazione nell'applicazione IoT Central:

  1. Passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Per targetTemperature (termostato1) sostituire Temperatura di destinazione con Temperatura di destinazione (1).For targetTemperature (thermostat1), replace Target Temperature with Target Temperature (1).

  3. Per targetTemperature (termostato2), sostituire Target Temperature con Target Temperature (2).

  4. Seleziona Salva.

I componenti termostato nel modello Temperature Controller includono la proprietà scrivibile Target Temperature , il modello di dispositivo include la proprietà cloud Customer Name . Creare una visualizzazione che può essere usata da un operatore per modificare queste proprietà:

  1. Selezionare Visualizzazioni e quindi il riquadro Modifica dei dati del dispositivo e del cloud.

  2. Immettere Properties come nome del modulo.

  3. Selezionare le proprietà Target Temperature (1), Target Temperature (2) e Customer Name (Nome cliente). Fare quindi clic su Aggiungi sezione.

  4. Salva le modifiche.

Screenshot che mostra una visualizzazione per l'aggiornamento dei valori delle proprietà.

Pubblicare il modello di dispositivo

Prima che un operatore possa visualizzare e usare le personalizzazioni apportate, è necessario pubblicare il modello di dispositivo.

Nel modello di dispositivo Termostato selezionare Pubblica. Nel pannello Pubblica questo modello di dispositivo nell'applicazione selezionare Pubblica.

Un operatore può ora usare la visualizzazione Proprietà per aggiornare i valori delle proprietà e chiamare i comandi denominati Get thermostat1 status report (Ottieni report sullo stato del termostato1) e Get thermostat2 status report (Ottieni report sullo stato del termostato2) nella pagina dei comandi del dispositivo:

  • Aggiornare i valori delle proprietà scrivibili nella pagina Proprietà :

    Screenshot che mostra l'aggiornamento delle proprietà del dispositivo.

  • Chiamare i comandi dalla pagina Comandi . Se si esegue il comando report di stato, selezionare una data e un'ora per il parametro Since prima di eseguirlo:

    Screenshot che mostra la chiamata a un comando.

    Screenshot che mostra una risposta al comando.

È possibile visualizzare il modo in cui il dispositivo risponde ai comandi e agli aggiornamenti delle proprietà:

2021-03-30 15:43:57.133 DEBUG TemperatureController:309 - Command: Received - component="thermostat1", generating min, max, avg temperature report since Tue Mar 30 06:00:00 BST 2021
2021-03-30 15:43:57.153 DEBUG TemperatureController:332 - Command: MaxMinReport since Tue Mar 30 06:00:00 BST 2021: "maxTemp": 35.6°C, "minTemp": 35.6°C, "avgTemp": 35.6°C, "startTime": 2021-03-30T15:43:41Z, "endTime": 2021-03-30T15:43:56Z
2021-03-30 15:43:57.394 DEBUG TemperatureController:502 - Command - Response from IoT Hub: command name=null, status=OK_EMPTY


...

2021-03-30 15:48:47.808 DEBUG TemperatureController:372 - Property: Received - component="thermostat2", {"targetTemperature": 67.0°C}.
2021-03-30 15:48:47.837 DEBUG TemperatureController:382 - Property: Update - component="thermostat2", {"targetTemperature": 67.0°C} is IN_PROGRESS

Esplora il codice

Prerequisiti

Per seguire la procedura descritta in questo articolo, sono necessarie le risorse seguenti:

  • Un computer di sviluppo con Node.js versione 6 o successiva installato. Per controllare la versione, è possibile eseguire node --version nella riga di comando. Le istruzioni riportate in questa esercitazione presuppongono che il comando node venga eseguito al prompt dei comandi di Windows. È tuttavia possibile usare Node.js in numerosi altri sistemi operativi.

  • Una copia locale del repository GitHub Microsoft Azure IoT SDK per Node.js che contiene il codice di esempio. Usare questo collegamento per scaricare una copia del repository: Scaricare ZIP. Quindi decomprimere il file in un percorso appropriato nel computer locale.

Esaminare il codice

Nella copia di Microsoft Azure IoT SDK per Node.js scaricato in precedenza aprire il file azure-iot-sdk-node/device/samples/javascript/pnp_temperature_controller.js in un editor di testo.

L'esempio implementa il modello di linguaggio di definizione di Temperature Controller digital twin a più componenti.

Quando si esegue l'esempio per connettersi a IoT Central, il dispositivo viene registrato tramite il servizio Device Provisioning e viene generata una stringa di connessione. L'esempio recupera le informazioni necessarie sulla connessione del servizio Device Provisioning dall'ambiente di riga di comando.

Il metodo main:

  • Crea un oggetto client e imposta l'ID modello di dtmi:com:example:TemperatureController;2 prima di aprire la connessione. IoT Central usa l'ID modello per identificare o generare il modello di dispositivo per questo dispositivo. Per altre informazioni, vedere Assegnare un dispositivo a un modello di dispositivo.
  • Crea gestori di comandi per tre comandi.
  • Avvia un ciclo per ogni componente termostato per inviare dati di telemetria relativi alla temperatura ogni 5 secondi.
  • Avvia un ciclo per il componente predefinito per inviare i dati di telemetria delle dimensioni del set di lavoro ogni 6 secondi.
  • Invia la maxTempSinceLastReboot proprietà per ogni componente termostato.
  • Invia le proprietà delle informazioni sul dispositivo.
  • Crea gestori delle proprietà scrivibili per i tre componenti.
async function main() {
  // ...

  // fromConnectionString must specify a transport, coming from any transport package.
  const client = Client.fromConnectionString(deviceConnectionString, Protocol);
  console.log('Connecting using connection string: ' + deviceConnectionString);
  let resultTwin;

  try {
    // Add the modelId here
    await client.setOptions(modelIdObject);
    await client.open();
    console.log('Enabling the commands on the client');
    client.onDeviceMethod(commandNameGetMaxMinReport1, commandHandler);
    client.onDeviceMethod(commandNameGetMaxMinReport2, commandHandler);
    client.onDeviceMethod(commandNameReboot, commandHandler);

    // Send Telemetry after some interval
    let index1 = 0;
    let index2 = 0;
    let index3 = 0;
    intervalToken1 = setInterval(() => {
      const data = JSON.stringify(thermostat1.updateSensor().getCurrentTemperatureObject());
      sendTelemetry(client, data, index1, thermostat1ComponentName).catch((err) => console.log('error ', err.toString()));
      index1 += 1;
    }, 5000);

    intervalToken2 = setInterval(() => {
      const data = JSON.stringify(thermostat2.updateSensor().getCurrentTemperatureObject());
      sendTelemetry(client, data, index2, thermostat2ComponentName).catch((err) => console.log('error ', err.toString()));
      index2 += 1;
    }, 5500);


    intervalToken3 = setInterval(() => {
      const data = JSON.stringify({ workingset: 1 + (Math.random() * 90) });
      sendTelemetry(client, data, index3, null).catch((err) => console.log('error ', err.toString()));
      index3 += 1;
    }, 6000);

    // attach a standard input exit listener
    exitListener(client);

    try {
      resultTwin = await client.getTwin();
      // Only report readable properties
      const patchRoot = helperCreateReportedPropertiesPatch({ serialNumber: serialNumber }, null);
      const patchThermostat1Info = helperCreateReportedPropertiesPatch({
        maxTempSinceLastReboot: thermostat1.getMaxTemperatureValue(),
      }, thermostat1ComponentName);

      const patchThermostat2Info = helperCreateReportedPropertiesPatch({
        maxTempSinceLastReboot: thermostat2.getMaxTemperatureValue(),
      }, thermostat2ComponentName);

      const patchDeviceInfo = helperCreateReportedPropertiesPatch({
        manufacturer: 'Contoso Device Corporation',
        model: 'Contoso 47-turbo',
        swVersion: '10.89',
        osName: 'Contoso_OS',
        processorArchitecture: 'Contoso_x86',
        processorManufacturer: 'Contoso Industries',
        totalStorage: 65000,
        totalMemory: 640,
      }, deviceInfoComponentName);

      // the below things can only happen once the twin is there
      updateComponentReportedProperties(resultTwin, patchRoot, null);
      updateComponentReportedProperties(resultTwin, patchThermostat1Info, thermostat1ComponentName);
      updateComponentReportedProperties(resultTwin, patchThermostat2Info, thermostat2ComponentName);
      updateComponentReportedProperties(resultTwin, patchDeviceInfo, deviceInfoComponentName);
      desiredPropertyPatchListener(resultTwin, [thermostat1ComponentName, thermostat2ComponentName, deviceInfoComponentName]);
    } catch (err) {
      console.error('could not retrieve twin or report twin properties\n' + err.toString());
    }
  } catch (err) {
    console.error('could not connect Plug and Play client or could not attach interval function for telemetry\n' + err.toString());
  }
}

La funzione provisionDevice usa il servizio Device Provisioning per registrare il dispositivo e connetterlo a IoT Central. Il payload include l'ID modello usato da IoT Central per assegnare un dispositivo a un modello di dispositivo:

async function provisionDevice(payload) {
  var provSecurityClient = new SymmetricKeySecurityClient(registrationId, symmetricKey);
  var provisioningClient = ProvisioningDeviceClient.create(provisioningHost, idScope, new ProvProtocol(), provSecurityClient);

  if (payload) {
    provisioningClient.setProvisioningPayload(payload);
  }

  try {
    let result = await provisioningClient.register();
    deviceConnectionString = 'HostName=' + result.assignedHub + ';DeviceId=' + result.deviceId + ';SharedAccessKey=' + symmetricKey;
    console.log('registration succeeded');
    console.log('assigned hub=' + result.assignedHub);
    console.log('deviceId=' + result.deviceId);
    console.log('payload=' + JSON.stringify(result.payload));
  } catch (err) {
    console.error("error registering device: " + err.toString());
  }
}

La funzione sendTelemetry consente al dispositivo di inviare i dati di telemetria sulla temperatura a IoT Central. Per i dati di telemetria dai componenti, aggiunge una proprietà denominata $.sub con il nome del componente:

async function sendTelemetry(deviceClient, data, index, componentName) {
  if componentName) {
    console.log('Sending telemetry message %d from component: %s ', index, componentName);
  } else {
    console.log('Sending telemetry message %d from root interface', index);
  }
  const msg = new Message(data);
  if (componentName) {
    msg.properties.add(messageSubjectProperty, componentName);
  }
  msg.contentType = 'application/json';
  msg.contentEncoding = 'utf-8';
  await deviceClient.sendEvent(msg);
}

Il main metodo usa un metodo helper denominato helperCreateReportedPropertiesPatch per creare messaggi di aggiornamento delle proprietà. Questo metodo accetta un parametro facoltativo per specificare il componente che invia la proprietà :

const helperCreateReportedPropertiesPatch = (propertiesToReport, componentName) => {
  let patch;
  if (!!(componentName)) {
    patch = { };
    propertiesToReport.__t = 'c';
    patch[componentName] = propertiesToReport;
  } else {
    patch = { };
    patch = propertiesToReport;
  }
  if (!!(componentName)) {
    console.log('The following properties will be updated for component: ' + componentName);
  } else {
    console.log('The following properties will be updated for root interface.');
  }
  console.log(patch);
  return patch;
};

Il main metodo usa il metodo seguente per gestire gli aggiornamenti alle proprietà scrivibili da IoT Central. Si noti che il metodo compila la risposta con la versione e il codice di stato:

const desiredPropertyPatchListener = (deviceTwin, componentNames) => {
  deviceTwin.on('properties.desired', (delta) => {
    console.log('Received an update for device with value: ' + JSON.stringify(delta));
    Object.entries(delta).forEach(([key, values]) => {
      const version = delta.$version;
      if (!!(componentNames) && componentNames.includes(key)) { // then it is a component we are expecting
        const componentName = key;
        const patchForComponents = { [componentName]: {} };
        Object.entries(values).forEach(([propertyName, propertyValue]) => {
          if (propertyName !== '__t' && propertyName !== '$version') {
            console.log('Will update property: ' + propertyName + ' to value: ' + propertyValue + ' of component: ' + componentName);
            const propertyContent = { value: propertyValue };
            propertyContent.ac = 200;
            propertyContent.ad = 'Successfully executed patch';
            propertyContent.av = version;
            patchForComponents[componentName][propertyName] = propertyContent;
          }
        });
        updateComponentReportedProperties(deviceTwin, patchForComponents, componentName);
      }
      else if  (key !== '$version') { // individual property for root
        const patchForRoot = { };
        console.log('Will update property: ' + key + ' to value: ' + values + ' for root');
        const propertyContent = { value: values };
        propertyContent.ac = 200;
        propertyContent.ad = 'Successfully executed patch';
        propertyContent.av = version;
        patchForRoot[key] = propertyContent;
        updateComponentReportedProperties(deviceTwin, patchForRoot, null);
      }
    });
  });
};

Il main metodo usa i metodi seguenti per gestire i comandi da IoT Central:

const commandHandler = async (request, response) => {
  helperLogCommandRequest(request);
  switch (request.methodName) {
  case commandNameGetMaxMinReport1: {
    await sendCommandResponse(request, response, 200, thermostat1.getMaxMinReportObject());
    break;
  }
  case commandNameGetMaxMinReport2: {
    await sendCommandResponse(request, response, 200, thermostat2.getMaxMinReportObject());
    break;
  }
  case commandNameReboot: {
    await sendCommandResponse(request, response, 200, 'reboot response');
    break;
  }
  default:
    await sendCommandResponse(request, response, 404, 'unknown method');
    break;
  }
};

const sendCommandResponse = async (request, response, status, payload) => {
  try {
    await response.send(status, payload);
    console.log('Response to method: ' + request.methodName + ' sent successfully.' );
  } catch (err) {
    console.error('An error occurred when sending a method response:\n' + err.toString());
  }
};

Ottenere informazioni di connessione

Quando si eseguirà l'applicazione del dispositivo di esempio più avanti in questa esercitazione, saranno necessari i valori di configurazione seguenti:

  • Ambito ID: nell'applicazione IoT Central passare a Autorizzazioni > Gruppi di connessioni del dispositivo. Prendere nota del valore dell'Ambito ID.
  • Raggruppare la chiave primaria: nell'applicazione IoT Central passare a Autorizzazioni Gruppi > di connessioni > del dispositivo SAS-IoT-Devices. Prendere nota del valore Chiave primaria della firma di accesso condivisa.

Usare Azure Cloud Shell per generare una chiave del dispositivo dalla chiave primaria del gruppo recuperata:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Prendere nota della chiave dispositivo generata. Questo valore verrà usato in un passaggio successivo dell'esercitazione.

Nota

Per eseguire questo esempio, non è necessario registrare il dispositivo in anticipo nell'applicazione IoT Central. L'esempio usa la funzionalità IoT Central per registrare automaticamente i dispositivi quando si connettono per la prima volta.

Eseguire il codice

Per eseguire l'applicazione di esempio, aprire un ambiente della riga di comando e passare alla cartella azure-iot-sdk-node/device/samples/javascript contenente il file di esempio pnp_temperature_controller.js.

Impostare le variabili di ambiente per configurare l'esempio. Il frammento di codice seguente mostra come impostare le variabili di ambiente al prompt dei comandi di Windows. Se si usa una shell bash, sostituire i comandi set con i comandi export:

set IOTHUB_DEVICE_SECURITY_TYPE=DPS
set IOTHUB_DEVICE_DPS_ID_SCOPE=<The ID scope you made a note of previously>
set IOTHUB_DEVICE_DPS_DEVICE_ID=sample-device-01
set IOTHUB_DEVICE_DPS_DEVICE_KEY=<The generated device key you made a note of previously>
set IOTHUB_DEVICE_DPS_ENDPOINT=global.azure-devices-provisioning.net

Installare i pacchetti necessari:

npm install

Eseguire l'esempio:

node pnp_temperature_controller.js

L'output seguente mostra la registrazione del dispositivo e la connessione a IoT Central. L'esempio invia quindi la maxTempSinceLastReboot proprietà dai due componenti del termostato prima di iniziare a inviare i dati di telemetria:

registration succeeded
assigned hub=iotc-....azure-devices.net
deviceId=sample-device-01
payload=undefined
Connecting using connection string: HostName=iotc-....azure-devices.net;DeviceId=sample-device-01;SharedAccessKey=qdv...IpAo=
Enabling the commands on the client
Please enter q or Q to exit sample.
The following properties will be updated for root interface.
{ serialNumber: 'alwinexlepaho8329' }
The following properties will be updated for component: thermostat1
{ thermostat1: { maxTempSinceLastReboot: 1.5902294191855972, __t: 'c' } }
The following properties will be updated for component: thermostat2
{ thermostat2: { maxTempSinceLastReboot: 16.181771928614545, __t: 'c' } }
The following properties will be updated for component: deviceInformation
{ deviceInformation:
   { manufacturer: 'Contoso Device Corporation',
     model: 'Contoso 47-turbo',
     swVersion: '10.89',
     osName: 'Contoso_OS',
     processorArchitecture: 'Contoso_x86',
     processorManufacturer: 'Contoso Industries',
     totalStorage: 65000,
     totalMemory: 640,
     __t: 'c' } }
executed sample
Received an update for device with value: {"$version":1}
Properties have been reported for component: thermostat1
Properties have been reported for component: thermostat2
Properties have been reported for component: deviceInformation
Properties have been reported for root interface.
Sending telemetry message 0 from component: thermostat1 
Sending telemetry message 0 from component: thermostat2 
Sending telemetry message 0 from root interface

Nell'applicazione Azure IoT Central un operatore può:

  • Visualizzare i dati di telemetria inviati dai due componenti termostato nella pagina Panoramica :

    Screenshot che mostra la pagina di panoramica del dispositivo.

  • Visualizzare le proprietà del dispositivo nella pagina Informazioni. Questa pagina mostra le proprietà del componente informazioni sul dispositivo e i due componenti termostato:

    Screenshot che mostra la visualizzazione delle proprietà del dispositivo.

Personalizzare il modello di dispositivo

Gli sviluppatori di soluzioni possono personalizzare il modello di dispositivo creato automaticamente da IoT Central quando il dispositivo del controller di temperatura è connesso.

Per aggiungere una proprietà cloud per archiviare il nome del cliente associato al dispositivo:

  1. Nell'applicazione IoT Central passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Nel modello Temperature Controller selezionare +Aggiungi funzionalità.

  3. Immettere Nome cliente come nome visualizzato, selezionare Proprietà cloud come tipo di funzionalità, espandere la voce e scegliere String come schema. Quindi selezionare Salva.

Per personalizzare la modalità di visualizzazione dei comandi del report Get Max-Min nell'applicazione IoT Central:

  1. Passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Per getMaxMinReport (termostato1), sostituire Get Max-Min report.with Get thermostat1 status report.For get thermostatMinReport (thermostat1), replace Get Max-Min report.

  3. Per getMaxMinReport (termostato2), sostituire Get Max-Min report. With Get thermostat2 status report .For get thermostatMinReport (thermostat2), replace Get Max-Min report. with Get thermostat2 status report.

  4. Seleziona Salva.

Per personalizzare la visualizzazione delle proprietà scrivibili di Temperatura di destinazione nell'applicazione IoT Central:

  1. Passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Per targetTemperature (termostato1) sostituire Temperatura di destinazione con Temperatura di destinazione (1).For targetTemperature (thermostat1), replace Target Temperature with Target Temperature (1).

  3. Per targetTemperature (termostato2), sostituire Target Temperature con Target Temperature (2).

  4. Seleziona Salva.

I componenti termostato nel modello Temperature Controller includono la proprietà scrivibile Target Temperature , il modello di dispositivo include la proprietà cloud Customer Name . Creare una visualizzazione che può essere usata da un operatore per modificare queste proprietà:

  1. Selezionare Visualizzazioni e quindi il riquadro Modifica dei dati del dispositivo e del cloud.

  2. Immettere Properties come nome del modulo.

  3. Selezionare le proprietà Target Temperature (1), Target Temperature (2) e Customer Name (Nome cliente). Fare quindi clic su Aggiungi sezione.

  4. Salva le modifiche.

Screenshot che mostra una visualizzazione per l'aggiornamento dei valori delle proprietà.

Pubblicare il modello di dispositivo

Prima che un operatore possa visualizzare e usare le personalizzazioni apportate, è necessario pubblicare il modello di dispositivo.

Nel modello di dispositivo Termostato selezionare Pubblica. Nel pannello Pubblica questo modello di dispositivo nell'applicazione selezionare Pubblica.

Un operatore può ora usare la visualizzazione Proprietà per aggiornare i valori delle proprietà e chiamare i comandi denominati Get thermostat1 status report (Ottieni report sullo stato del termostato1) e Get thermostat2 status report (Ottieni report sullo stato del termostato2) nella pagina dei comandi del dispositivo:

  • Aggiornare i valori delle proprietà scrivibili nella pagina Proprietà :

    Screenshot che mostra l'aggiornamento delle proprietà del dispositivo.

  • Chiamare i comandi dalla pagina Comandi . Se si esegue il comando report di stato, selezionare una data e un'ora per il parametro Since prima di eseguirlo:

    Screenshot che mostra la chiamata a un comando.

    Screenshot che mostra una risposta al comando.

È possibile vedere in che modo il dispositivo risponde ai comandi e agli aggiornamenti delle proprietà. Il getMaxMinReport comando si trova nel thermostat2 componente , il reboot comando si trova nel componente predefinito. La targetTemperature proprietà scrivibile è stata impostata per il thermostat2 componente:

Received command request for command name: thermostat2*getMaxMinReport
The command request payload is:
2021-03-26T06:00:00.000Z
Response to method: thermostat2*getMaxMinReport sent successfully.

...

Received command request for command name: reboot
The command request payload is:
10
Response to method: reboot sent successfully.

...

Received an update for device with value: {"thermostat2":{"targetTemperature":76,"__t":"c"},"$version":2}
Will update property: targetTemperature to value: 76 of component: thermostat2
Properties have been reported for component: thermostat2

Esplora il codice

Prerequisiti

Per seguire la procedura descritta in questo articolo, sono necessarie le risorse seguenti:

  • Un computer di sviluppo con Python installato. Verifica i requisiti della versione corrente di Python consultando l'Azure IoT SDK per Python. Per controllare la versione, è possibile eseguire python --version nella riga di comando. Python è disponibile per un'ampia gamma di sistemi operativi. Le istruzioni riportate in questa esercitazione presuppongono che il comando python venga eseguito al prompt dei comandi di Windows.

  • Una copia locale del repository GitHub Microsoft Azure IoT SDK per Python che contiene il codice di esempio. Usare questo collegamento per scaricare una copia del repository: Scaricare ZIP. Quindi decomprimere il file in un percorso appropriato nel computer locale.

Esaminare il codice

Nella copia di Microsoft Azure IoT SDK per Python scaricata in precedenza aprire il file azure-iot-sdk-python/samples/pnp/temp_controller_with_thermostats.py in un editor di testo.

L'esempio implementa il modello di linguaggio di definizione di Temperature Controller digital twin a più componenti.

Quando si esegue l'esempio per connettersi a IoT Central, il dispositivo viene registrato tramite il servizio Device Provisioning e viene generata una stringa di connessione. L'esempio recupera le informazioni necessarie sulla connessione del servizio Device Provisioning dall'ambiente di riga di comando.

La funzione main:

  • Usa il servizio Device Provisioning per effettuare il provisioning del dispositivo. Le informazioni sul provisioning includono l'ID modello. IoT Central usa l'ID modello per identificare o generare il modello di dispositivo per questo dispositivo. Per altre informazioni, vedere Assegnare un dispositivo a un modello di dispositivo.
  • Crea un oggetto Device_client e imposta l'ID modello di dtmi:com:example:TemperatureController;2 prima di aprire la connessione.
  • Invia i valori iniziali delle proprietà a IoT Central. Usa per pnp_helper creare le patch.
  • Crea listener per i getMaxMinReport comandi e reboot . Ogni componente termostato ha un proprio getMaxMinReport comando.
  • Crea un listener di proprietà, per restare in ascolto degli aggiornamenti delle proprietà scrivibili.
  • Avvia un ciclo per inviare dati di telemetria relativi alla temperatura dai due componenti del termostato e i dati di telemetria del working set dal componente predefinito ogni 8 secondi.
async def main():
    switch = os.getenv("IOTHUB_DEVICE_SECURITY_TYPE")
    if switch == "DPS":
        provisioning_host = (
            os.getenv("IOTHUB_DEVICE_DPS_ENDPOINT")
            if os.getenv("IOTHUB_DEVICE_DPS_ENDPOINT")
            else "global.azure-devices-provisioning.net"
        )
        id_scope = os.getenv("IOTHUB_DEVICE_DPS_ID_SCOPE")
        registration_id = os.getenv("IOTHUB_DEVICE_DPS_DEVICE_ID")
        symmetric_key = os.getenv("IOTHUB_DEVICE_DPS_DEVICE_KEY")

        registration_result = await provision_device(
            provisioning_host, id_scope, registration_id, symmetric_key, model_id
        )

        if registration_result.status == "assigned":
            print("Device was assigned")
            print(registration_result.registration_state.assigned_hub)
            print(registration_result.registration_state.device_id)
            device_client = IoTHubDeviceClient.create_from_symmetric_key(
                symmetric_key=symmetric_key,
                hostname=registration_result.registration_state.assigned_hub,
                device_id=registration_result.registration_state.device_id,
                product_info=model_id,
            )
        else:
            raise RuntimeError(
                "Could not provision device. Aborting Plug and Play device connection."
            )

    elif switch == "connectionString":
        # ...

    # Connect the client.
    await device_client.connect()

    ################################################
    # Update readable properties from various components

    properties_root = pnp_helper.create_reported_properties(serialNumber=serial_number)
    properties_thermostat1 = pnp_helper.create_reported_properties(
        thermostat_1_component_name, maxTempSinceLastReboot=98.34
    )
    properties_thermostat2 = pnp_helper.create_reported_properties(
        thermostat_2_component_name, maxTempSinceLastReboot=48.92
    )
    properties_device_info = pnp_helper.create_reported_properties(
        device_information_component_name,
        swVersion="5.5",
        manufacturer="Contoso Device Corporation",
        model="Contoso 4762B-turbo",
        osName="Mac Os",
        processorArchitecture="x86-64",
        processorManufacturer="Intel",
        totalStorage=1024,
        totalMemory=32,
    )

    property_updates = asyncio.gather(
        device_client.patch_twin_reported_properties(properties_root),
        device_client.patch_twin_reported_properties(properties_thermostat1),
        device_client.patch_twin_reported_properties(properties_thermostat2),
        device_client.patch_twin_reported_properties(properties_device_info),
    )

    ################################################
    # Get all the listeners running
    print("Listening for command requests and property updates")

    global THERMOSTAT_1
    global THERMOSTAT_2
    THERMOSTAT_1 = Thermostat(thermostat_1_component_name, 10)
    THERMOSTAT_2 = Thermostat(thermostat_2_component_name, 10)

    listeners = asyncio.gather(
        execute_command_listener(
            device_client, method_name="reboot", user_command_handler=reboot_handler
        ),
        execute_command_listener(
            device_client,
            thermostat_1_component_name,
            method_name="getMaxMinReport",
            user_command_handler=max_min_handler,
            create_user_response_handler=create_max_min_report_response,
        ),
        execute_command_listener(
            device_client,
            thermostat_2_component_name,
            method_name="getMaxMinReport",
            user_command_handler=max_min_handler,
            create_user_response_handler=create_max_min_report_response,
        ),
        execute_property_listener(device_client),
    )

    ################################################
    # Function to send telemetry every 8 seconds

    async def send_telemetry():
        print("Sending telemetry from various components")

        while True:
            curr_temp_ext = random.randrange(10, 50)
            THERMOSTAT_1.record(curr_temp_ext)

            temperature_msg1 = {"temperature": curr_temp_ext}
            await send_telemetry_from_temp_controller(
                device_client, temperature_msg1, thermostat_1_component_name
            )

            curr_temp_int = random.randrange(10, 50)  # Current temperature in Celsius
            THERMOSTAT_2.record(curr_temp_int)

            temperature_msg2 = {"temperature": curr_temp_int}

            await send_telemetry_from_temp_controller(
                device_client, temperature_msg2, thermostat_2_component_name
            )

            workingset_msg3 = {"workingSet": random.randrange(1, 100)}
            await send_telemetry_from_temp_controller(device_client, workingset_msg3)

    send_telemetry_task = asyncio.ensure_future(send_telemetry())

    # ...

La funzione provision_device usa il servizio Device Provisioning per effettuare il provisioning del dispositivo e registrarlo con IoT Central. La funzione include l'ID del modello di dispositivo, usato da IoT Central per assegnare un dispositivo a un modello di dispositivo, nel payload di provisioning:

async def provision_device(provisioning_host, id_scope, registration_id, symmetric_key, model_id):
    provisioning_device_client = ProvisioningDeviceClient.create_from_symmetric_key(
        provisioning_host=provisioning_host,
        registration_id=registration_id,
        id_scope=id_scope,
        symmetric_key=symmetric_key,
    )

    provisioning_device_client.provisioning_payload = {"modelId": model_id}
    return await provisioning_device_client.register()

La execute_command_listener funzione gestisce le richieste di comando, esegue la max_min_handler funzione quando il dispositivo riceve il getMaxMinReport comando per i componenti termostato e la reboot_handler funzione quando il dispositivo riceve il reboot comando. Usa il pnp_helper modulo per compilare la risposta:

async def execute_command_listener(
    device_client,
    component_name=None,
    method_name=None,
    user_command_handler=None,
    create_user_response_handler=None,
):
    while True:
        if component_name and method_name:
            command_name = component_name + "*" + method_name
        elif method_name:
            command_name = method_name
        else:
            command_name = None

        command_request = await device_client.receive_method_request(command_name)
        print("Command request received with payload")
        values = command_request.payload
        print(values)

        if user_command_handler:
            await user_command_handler(values)
        else:
            print("No handler provided to execute")

        (response_status, response_payload) = pnp_helper.create_response_payload_with_status(
            command_request, method_name, create_user_response=create_user_response_handler
        )

        command_response = MethodResponse.create_from_method_request(
            command_request, response_status, response_payload
        )

        try:
            await device_client.send_method_response(command_response)
        except Exception:
            print("responding to the {command} command failed".format(command=method_name))

Gestisce async def execute_property_listener gli aggiornamenti delle proprietà scrivibili, ad targetTemperature esempio per i componenti termostato e genera la risposta JSON. Usa il pnp_helper modulo per compilare la risposta:

async def execute_property_listener(device_client):
    while True:
        patch = await device_client.receive_twin_desired_properties_patch()  # blocking call
        print(patch)
        properties_dict = pnp_helper.create_reported_properties_from_desired(patch)

        await device_client.patch_twin_reported_properties(properties_dict)

La send_telemetry_from_temp_controller funzione invia i messaggi di telemetria dai componenti termostato a IoT Central. Usa il pnp_helper modulo per compilare i messaggi:

async def send_telemetry_from_temp_controller(device_client, telemetry_msg, component_name=None):
    msg = pnp_helper.create_telemetry(telemetry_msg, component_name)
    await device_client.send_message(msg)
    print("Sent message")
    print(msg)
    await asyncio.sleep(5)

Ottenere informazioni di connessione

Quando si eseguirà l'applicazione del dispositivo di esempio più avanti in questa esercitazione, saranno necessari i valori di configurazione seguenti:

  • Ambito ID: nell'applicazione IoT Central passare a Autorizzazioni > Gruppi di connessioni del dispositivo. Prendere nota del valore dell'Ambito ID.
  • Raggruppare la chiave primaria: nell'applicazione IoT Central passare a Autorizzazioni Gruppi > di connessioni > del dispositivo SAS-IoT-Devices. Prendere nota del valore Chiave primaria della firma di accesso condivisa.

Usare Azure Cloud Shell per generare una chiave del dispositivo dalla chiave primaria del gruppo recuperata:

az extension add --name azure-iot
az iot central device compute-device-key --device-id sample-device-01 --pk <the group primary key value>

Prendere nota della chiave dispositivo generata. Questo valore verrà usato in un passaggio successivo dell'esercitazione.

Nota

Per eseguire questo esempio, non è necessario registrare il dispositivo in anticipo nell'applicazione IoT Central. L'esempio usa la funzionalità IoT Central per registrare automaticamente i dispositivi quando si connettono per la prima volta.

Eseguire il codice

Per eseguire l'applicazione di esempio, aprire un ambiente della riga di comando e passare alla cartella azure-iot-sdk-python-2/samples/pnp contenente il file di esempio temp_controller_with_thermostats.py .

Impostare le variabili di ambiente per configurare l'esempio. Il frammento di codice seguente mostra come impostare le variabili di ambiente al prompt dei comandi di Windows. Se si usa una shell bash, sostituire i comandi set con i comandi export:

set IOTHUB_DEVICE_SECURITY_TYPE=DPS
set IOTHUB_DEVICE_DPS_ID_SCOPE=<The ID scope you made a note of previously>
set IOTHUB_DEVICE_DPS_DEVICE_ID=sample-device-01
set IOTHUB_DEVICE_DPS_DEVICE_KEY=<The generated device key you made a note of previously>
set IOTHUB_DEVICE_DPS_ENDPOINT=global.azure-devices-provisioning.net

Installare i pacchetti necessari:

pip install azure-iot-device

Eseguire l'esempio:

python temp_controller_with_thermostats.py

L'output seguente mostra la registrazione del dispositivo e la connessione a IoT Central. L'esempio invia le maxTempSinceLastReboot proprietà dai due componenti del termostato prima di iniziare a inviare i dati di telemetria:

Device was assigned
iotc-60a.....azure-devices.net
sample-device-01
Updating pnp properties for root interface
{'serialNumber': 'alohomora'}
Updating pnp properties for thermostat1
{'thermostat1': {'maxTempSinceLastReboot': 98.34, '__t': 'c'}}
Updating pnp properties for thermostat2
{'thermostat2': {'maxTempSinceLastReboot': 48.92, '__t': 'c'}}
Updating pnp properties for deviceInformation
{'deviceInformation': {'swVersion': '5.5', 'manufacturer': 'Contoso Device Corporation', 'model': 'Contoso 4762B-turbo', 'osName': 'Mac Os', 'processorArchitecture': 'x86-64', 'processorManufacturer': 'Intel', 'totalStorage': 1024, 'totalMemory': 32, '__t': 'c'}}
Listening for command requests and property updates
Press Q to quit
Sending telemetry from various components
Sent message
{"temperature": 27}
Sent message
{"temperature": 17}
Sent message
{"workingSet": 13}

Nell'applicazione Azure IoT Central un operatore può:

  • Visualizzare i dati di telemetria inviati dai due componenti termostato nella pagina Panoramica :

    Screenshot che mostra la pagina di panoramica del dispositivo.

  • Visualizzare le proprietà del dispositivo nella pagina Informazioni. Questa pagina mostra le proprietà del componente informazioni sul dispositivo e i due componenti termostato:

    Screenshot che mostra la visualizzazione delle proprietà del dispositivo.

Personalizzare il modello di dispositivo

Gli sviluppatori di soluzioni possono personalizzare il modello di dispositivo creato automaticamente da IoT Central quando il dispositivo del controller di temperatura è connesso.

Per aggiungere una proprietà cloud per archiviare il nome del cliente associato al dispositivo:

  1. Nell'applicazione IoT Central passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Nel modello Temperature Controller selezionare +Aggiungi funzionalità.

  3. Immettere Nome cliente come nome visualizzato, selezionare Proprietà cloud come tipo di funzionalità, espandere la voce e scegliere String come schema. Quindi selezionare Salva.

Per personalizzare la modalità di visualizzazione dei comandi del report Get Max-Min nell'applicazione IoT Central:

  1. Passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Per getMaxMinReport (termostato1), sostituire Get Max-Min report.with Get thermostat1 status report.For get thermostatMinReport (thermostat1), replace Get Max-Min report.

  3. Per getMaxMinReport (termostato2), sostituire Get Max-Min report. With Get thermostat2 status report .For get thermostatMinReport (thermostat2), replace Get Max-Min report. with Get thermostat2 status report.

  4. Seleziona Salva.

Per personalizzare la visualizzazione delle proprietà scrivibili di Temperatura di destinazione nell'applicazione IoT Central:

  1. Passare al modello di dispositivo Temperature Controller nella pagina Modelli di dispositivo.

  2. Per targetTemperature (termostato1) sostituire Temperatura di destinazione con Temperatura di destinazione (1).For targetTemperature (thermostat1), replace Target Temperature with Target Temperature (1).

  3. Per targetTemperature (termostato2), sostituire Target Temperature con Target Temperature (2).

  4. Seleziona Salva.

I componenti termostato nel modello Temperature Controller includono la proprietà scrivibile Target Temperature , il modello di dispositivo include la proprietà cloud Customer Name . Creare una visualizzazione che può essere usata da un operatore per modificare queste proprietà:

  1. Selezionare Visualizzazioni e quindi il riquadro Modifica dei dati del dispositivo e del cloud.

  2. Immettere Properties come nome del modulo.

  3. Selezionare le proprietà Target Temperature (1), Target Temperature (2) e Customer Name (Nome cliente). Fare quindi clic su Aggiungi sezione.

  4. Salva le modifiche.

Screenshot che mostra una visualizzazione per l'aggiornamento dei valori delle proprietà.

Pubblicare il modello di dispositivo

Prima che un operatore possa visualizzare e usare le personalizzazioni apportate, è necessario pubblicare il modello di dispositivo.

Nel modello di dispositivo Termostato selezionare Pubblica. Nel pannello Pubblica questo modello di dispositivo nell'applicazione selezionare Pubblica.

Un operatore può ora usare la visualizzazione Proprietà per aggiornare i valori delle proprietà e chiamare i comandi denominati Get thermostat1 status report (Ottieni report sullo stato del termostato1) e Get thermostat2 status report (Ottieni report sullo stato del termostato2) nella pagina dei comandi del dispositivo:

  • Aggiornare i valori delle proprietà scrivibili nella pagina Proprietà :

    Screenshot che mostra l'aggiornamento delle proprietà del dispositivo.

  • Chiamare i comandi dalla pagina Comandi . Se si esegue il comando report di stato, selezionare una data e un'ora per il parametro Since prima di eseguirlo:

    Screenshot che mostra la chiamata a un comando.

    Screenshot che mostra una risposta al comando.

È possibile visualizzare il modo in cui il dispositivo risponde ai comandi e agli aggiornamenti delle proprietà:

{'thermostat1': {'targetTemperature': 67, '__t': 'c'}, '$version': 2}
the data in the desired properties patch was: {'thermostat1': {'targetTemperature': 67, '__t': 'c'}, '$version': 2}
Values received are :-
{'targetTemperature': 67, '__t': 'c'}
Sent message

...

Command request received with payload
2021-03-31T05:00:00.000Z
Will return the max, min and average temperature from the specified time 2021-03-31T05:00:00.000Z to the current time
Done generating
{"avgTemp": 4.0, "endTime": "2021-03-31T12:29:48.322427", "maxTemp": 18, "minTemp": null, "startTime": "2021-03-31T12:28:28.322381"}

Visualizzare dati non elaborati

È possibile usare la visualizzazione Dati non elaborati per esaminare i dati non elaborati inviati dal dispositivo a IoT Central:

Screenshot che mostra la visualizzazione dati non elaborata.

In questa visualizzazione è possibile selezionare le colonne da visualizzare e impostare un intervallo di tempo da visualizzare. La colonna Dati non modellati mostra i dati del dispositivo che non corrispondono ad alcuna definizione di proprietà o telemetria nel modello di dispositivo.

Pulire le risorse

Se non si prevede di completare altri argomenti di avvio rapido o esercitazioni, è possibile eliminare l'applicazione IoT Central:

  1. Nell'applicazione IoT Central, passare a Applicazione > Gestione .
  2. Selezionare Elimina e quindi confermare l'azione.

Passaggi successivi

Se si preferisce continuare il set di esercitazioni di IoT Central e saperne di più sulla creazione di una soluzione di IoT Central, vedere: