Oktatóanyag: Ügyfélalkalmazás létrehozása és csatlakoztatása az Azure IoT Central-alkalmazáshoz

Ez az oktatóanyag bemutatja, hogyan csatlakoztathat egy ügyfélalkalmazást az Azure IoT Central-alkalmazáshoz. Az alkalmazás egy hőmérséklet-vezérlő eszköz viselkedését szimulálja. Amikor az alkalmazás csatlakozik az IoT Centralhoz, elküldi a hőmérséklet-vezérlő eszközmodelljének modellazonosítóját. Az IoT Central a modellazonosítóval kéri le az eszközmodellt, és létrehoz egy eszközsablont. Nézeteket adhat hozzá az eszközsablonhoz, hogy az operátorok interakcióba léphessenek egy eszközzel.

Ebben az oktatóanyagban az alábbiakkal fog megismerkedni:

  • Hozza létre és futtassa az eszközkódot, és tekintse meg, hogy csatlakozik az IoT Central-alkalmazáshoz.
  • Tekintse meg az eszközről küldött szimulált telemetriát.
  • Egyéni nézetek hozzáadása eszközsablonhoz.
  • Tegye közzé az eszközsablont.
  • Nézetben kezelheti az eszköz tulajdonságait.
  • Parancs meghívása az eszköz vezérléséhez.

Browse code

Előfeltételek

Az oktatóanyag lépéseinek elvégzéséhez a következőkre van szüksége:

  • Aktív Azure-előfizetés. Ha még nincs Azure-előfizetése, kezdés előtt hozzon létre egy ingyenes fiókot.

  • Az egyéni alkalmazássablonból létrehozott IoT Central-alkalmazás . További információ: IoT Central-alkalmazás létrehozása és az alkalmazás ismertetése.

Ezt az oktatóanyagot Linuxon vagy Windowson is futtathatja. Az oktatóanyagban szereplő rendszerhéjparancsok a ""/ elérési útelválasztók Linux-konvencióját követik, ha a Windowson végig követi őket, mindenképpen cserélje fel ezeket az elválasztókat a következőre: ""\.

Az előfeltételek az operációs rendszertől függően eltérnek:

Linux

Ez az oktatóanyag feltételezi, hogy Ubuntu Linuxot használ. Az oktatóanyag lépéseit az Ubuntu 18.04-ben teszteltük.

Az oktatóanyag Linuxon való elvégzéséhez telepítse a következő szoftvert a helyi Linux-környezetbe:

Telepítse a GCC-t, a Gitet, a cmake-t és az összes szükséges függőséget a apt-get következő paranccsal:

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

Ellenőrizze, hogy a verzió nagyobb-ecmake, mint 2.8.12, és a GCC verziója nagyobb-e, mint 4.4.7.

cmake --version
gcc --version

Ablakok

Az oktatóanyag Windows rendszeren való elvégzéséhez telepítse a következő szoftvert a helyi Windows-környezetbe:

A kód letöltése

Ebben az oktatóanyagban egy fejlesztési környezetet készít elő, a segítségével klónozhat és létrehozhatja az Azure IoT Hub Device C SDK-t.

Nyisson meg egy parancssort a választott könyvtárban. Hajtsa végre a következő parancsot az Azure IoT C SDK-k és -tárak GitHub-adattárának klónozásához erre a helyre:

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

A művelet végrehajtása várhatóan több percet vesz igénybe.

A kód áttekintése

A korábban letöltött Microsoft Azure IoT SDK for C példányában nyissa meg az azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_temperature_controller.c és az azure-iot-sdk-c/iothub_client/samples/pnp/pnp_temperature_controller/pnp_thermostat_component.c fájlokat egy szövegszerkesztőben.

A minta implementálja a többkomponensű Temperature Controller Digital Twin Definition Language modellt.

Amikor a mintát az IoT Centralhoz való csatlakozáshoz futtatja, az a Device Provisioning Service (DPS) használatával regisztrálja az eszközt, és létrehoz egy kapcsolati sztring. A minta lekéri a szükséges DPS-kapcsolati adatokat a parancssori környezetből.

A pnp_temperature_controller.c-ben a függvény először a main következőhöz hívja meg a következőtCreateDeviceClientAndAllocateComponents:

  • Adja meg a dtmi:com:example:Thermostat;1 modell azonosítóját. Az IoT Central a modellazonosítóval azonosítja vagy létrehozza az eszközhöz tartozó eszközsablont. További információ: Eszköz hozzárendelése eszközsablonhoz.
  • A DPS használatával kiépíteni és regisztrálni lehet az eszközt.
  • Hozzon létre egy eszközügyfél-leírót, és csatlakozzon az IoT Central-alkalmazáshoz.
  • Létrehoz egy kezelőt a hőmérséklet-vezérlő összetevő parancsaihoz.
  • Létrehoz egy kezelőt a hőmérséklet-vezérlő összetevő tulajdonságfrissítéseihez.
  • Létrehozza a két termosztát-összetevőt.

A main függvény következő lépése:

  • Az összes összetevő néhány kezdeti tulajdonságértékét jelenti.
  • Elindít egy hurkot, amely telemetriát küld az összes összetevőből.

A main függvény ezután elindít egy szálat a telemetriai adatok rendszeres küldéséhez.

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;
}

A pnp_thermostat_component.cfüggvény bemutatja PnP_ThermostatComponent_SendCurrentTemperature , hogy az eszköz hogyan küldi el a hőmérsékleti telemetriát egy összetevőről az IoT Centralnak:

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);
}

Ebben pnp_thermostat_component.ca függvény tulajdonságfrissítést maxTempSinceLastRebootPnP_TempControlComponent_Report_MaxTempSinceLastReboot_Property küld az összetevőből az IoT Centralnak:

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);
    }
}

Ebben pnp_thermostat_component.ca függvény az PnP_ThermostatComponent_ProcessPropertyUpdate IoT Central írható tulajdonságfrissítéseit kezeli:

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);
            }
        }
    }
}

Ebben pnp_thermostat_component.ca függvény az PnP_ThermostatComponent_ProcessCommand IoT Centralról hívott parancsokat kezeli:

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;
    }
}

A kód létrehozása

Az eszköz SDK-jával hozza létre a mellékelt mintakódot:

  1. Hozzon létre egy cmake alkönyvtárat az eszköz SDK gyökérmappájában, és keresse meg a mappát:

    cd azure-iot-sdk-c
    mkdir cmake
    cd cmake
    
  2. Futtassa az alábbi parancsokat az SDK és a minták létrehozásához:

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

Kapcsolatadatok lekérése

Amikor az oktatóanyag későbbi részében futtatja a mintaeszköz-alkalmazást, a következő konfigurációs értékekre lesz szüksége:

  • Azonosító hatóköre: Az IoT Central-alkalmazásban keresse meg az Engedélyek > eszköz kapcsolatcsoportokat. Jegyezze fel az azonosító hatókörének értékét.
  • Elsődleges csoportkulcs: Az IoT Central-alkalmazásban keresse meg az Engedélyek > eszköz kapcsolatcsoportokat > SAS-IoT-Devices. Jegyezze fel a közös hozzáférésű jogosultságkód elsődleges kulcsértékét .

Az Azure Cloud Shell használatával hozzon létre egy eszközkulcsot a lekért elsődleges csoportkulcsból:

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

Jegyezze fel a létrehozott eszközkulcsot, amelyet az oktatóanyag későbbi részében fog használni.

Feljegyzés

A minta futtatásához nem kell előre regisztrálnia az eszközt az IoT Central-alkalmazásban. A minta az IoT Central funkcióval automatikusan regisztrálja az eszközöket , amikor első alkalommal csatlakoznak.

A kód futtatása

A mintaalkalmazás futtatásához nyisson meg egy parancssori környezetet, és keresse meg az azure-iot-sdk-c\cmake mappát.

Állítsa be a környezeti változókat a minta konfigurálásához. Az alábbi kódrészlet bemutatja, hogyan állíthatja be a környezeti változókat a Windows parancssorában. Ha bash-rendszerhéjat használ, cserélje le a parancsokat parancsra setexport:

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

A minta futtatása:

# 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

Az alábbi kimenet az IoT Centralhoz való regisztrálást és csatlakozást mutatja. A minta elkezd telemetriai adatokat küldeni:

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

Az Azure IoT Central-alkalmazás operátoraként a következőkre van lehetőség:

  • Tekintse meg a két termosztát-összetevő által küldött telemetriát az Áttekintés lapon:

    Screenshot that shows the device overview page.

  • Az eszköz tulajdonságainak megtekintése a Névjegy lapon. Ezen az oldalon az eszközinformációs összetevő és a két termosztát-összetevő tulajdonságai láthatók:

    Screenshot that shows the device properties view.

Az eszközsablon testreszabása

Megoldásfejlesztőként testre szabhatja az IoT Central által automatikusan létrehozott eszközsablont, amikor a hőmérséklet-vezérlő eszköz csatlakoztatva van.

Felhőtulajdonság hozzáadása az eszközhöz társított ügyfélnév tárolásához:

  1. Az IoT Central-alkalmazásban lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. A Hőmérséklet-vezérlő modellben válassza a +Képesség hozzáadása lehetőséget.

  3. Adja meg az ügyfél nevét megjelenítési névként, válassza a Cloud tulajdonságot képességtípusként, bontsa ki a bejegyzést, és válassza a Sztringet sémaként. Ezután válassza a Mentés lehetőséget.

A Max-Min jelentés lekérése parancsok megjelenítésének testreszabása az IoT Central-alkalmazásban:

  1. Lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. A getMaxMinReport (termosztát1) esetében cserélje le a Max-Min jelentés lekérése elemet a Termosztát1 állapotjelentésre.

  3. A getMaxMinReport (termosztát2) esetében cserélje le a Max-Min jelentés lekérése elemet a Termosztát2 állapotjelentésre.

  4. Válassza a Mentés lehetőséget.

A Célhőmérséklet írható tulajdonságainak megjelenítésének testreszabása az IoT Central-alkalmazásban:

  1. Lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. TargetTemperature (termosztát1) esetén cserélje le a célhőmérsékletet a Célhőmérséklet (1) kifejezésre.

  3. TargetTemperature (termosztát2) esetén cserélje le a célhőmérsékletetcélhőmérsékletre (2).

  4. Válassza a Mentés lehetőséget.

A Hőmérséklet-vezérlő modell termosztát-összetevői tartalmazzák a Target Temperature írható tulajdonságot, az eszközsablon pedig az Ügyfélnév felhő tulajdonságot. Hozzon létre egy nézetet, amellyel az operátor szerkesztheti ezeket a tulajdonságokat:

  1. Válassza a Nézetek lehetőséget, majd válassza a Szerkesztés eszköz és a felhőbeli adatcsempét .

  2. Adja meg a Tulajdonságok nevet az űrlap neveként.

  3. Válassza ki a Célhőmérséklet (1), a Célhőmérséklet (2) és az Ügyfélnév tulajdonságokat. Ezután válassza a Szakasz hozzáadása lehetőséget.

  4. Mentse a módosításokat.

Screenshot that shows a view for updating property values.

Az eszközsablon közzététele

Ahhoz, hogy egy operátor láthassa és használhassa a végrehajtott testreszabásokat, közzé kell tennie az eszközsablont.

A Termosztát eszközsablonban válassza a Közzététel lehetőséget. Az eszközsablon közzététele az alkalmazáspanelen válassza a Közzététel lehetőséget.

Az operátor mostantól a Tulajdonságok nézetben frissítheti a tulajdonságértékeket, és meghívhatja a Termosztát1 állapotjelentés lekérése és a Termosztát2 állapotjelentés lekérése parancsokat az eszközparancsok oldalán:

  • A Írható tulajdonságértékek frissítése a Tulajdonságok lapon:

    Screenshot that shows updating the device properties.

  • Hívja meg a parancsokat a Parancsok lapon. Ha az állapotjelentés parancsot futtatja, a futtatás előtt válassza ki a Since paraméter dátumát és időpontját:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Láthatja, hogyan reagál az eszköz a parancsokra és a tulajdonságfrissítésekre:

<- 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

Browse code

Előfeltételek

A cikk lépéseinek elvégzéséhez a következő erőforrásokra van szüksége:

  • Aktív Azure-előfizetés. Ha még nincs Azure-előfizetése, kezdés előtt hozzon létre egy ingyenes fiókot.

  • Az egyéni alkalmazássablonból létrehozott IoT Central-alkalmazás . További információ: IoT Central-alkalmazás létrehozása és az alkalmazás ismertetése.

A kód áttekintése

A korábban letöltött Microsoft Azure IoT SDK for C#-adattár másolatában nyissa meg az azure-iot-sdk-csharp-main\azureiot.sln megoldásfájlt a Visual Studióban. A Megoldáskezelő bontsa ki a PnpDeviceSamples > TemperatureController mappát, és nyissa meg a Program.cs és TemperatureControllerSample.cs fájlokat a minta kódjának megtekintéséhez.

A minta implementálja a többkomponensű Temperature Controller Digital Twin Definition Language modellt.

Amikor a mintát az IoT Centralhoz való csatlakozáshoz futtatja, az a Device Provisioning Service (DPS) használatával regisztrálja az eszközt, és létrehoz egy kapcsolati sztring. A minta lekéri a dps-kapcsolat szükséges adatait a környezetből.

A Program.cs metódus a Main következőhöz hívSetupDeviceClientAsync:

  • Használja a modellazonosítót dtmi:com:example:TemperatureController;2 , amikor az eszközt a DPS-vel helyezi üzembe. Az IoT Central a modellazonosítóval azonosítja vagy létrehozza az eszközhöz tartozó eszközsablont. További információ: Eszköz hozzárendelése eszközsablonhoz.
  • Hozzon létre egy DeviceClient-példányt az IoT Centralhoz való csatlakozáshoz.
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;
}

A fő metódus ezután létrehoz egy TemperatureControllerSample-példányt , és meghívja a PerformOperationsAsync metódust az IoT Centrallal folytatott interakciók kezelésére.

A TemperatureControllerSample.cs a PerformOperationsAsync metódus:

  • Beállít egy kezelőt az alapértelmezett összetevő újraindítási parancsához.
  • Beállítja a getMaxMinReport parancsok kezelőit a két termosztát-összetevőn.
  • Beállítja a kezelőket, hogy fogadják a célhőmérséklet-tulajdonság frissítéseit a két termosztát-összetevőn.
  • Kezdeti eszközinformációs tulajdonságfrissítéseket küld.
  • Rendszeres időközönként hőmérséklet-telemetriát küld a két termosztát-összetevőből.
  • Rendszeresen küld munkakészlet-telemetriát az alapértelmezett összetevőről.
  • Az utolsó újraindítás óta a maximális hőmérsékletet küldi el, amikor a két termosztát-összetevő új maximális hőmérsékletet ér el.
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);
  }
}

A SendTemperatureAsync módszer azt mutatja be, hogy az eszköz hogyan küldi el a hőmérsékleti telemetriát egy összetevőről az IoT Centralnak. A SendTemperatureTelemetryAsync metódus az PnpConvention osztály használatával hozza létre az üzenetet:

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 },
      });
  }
}

A UpdateMaxTemperatureSinceLastRebootAsync metódus tulajdonságfrissítést maxTempSinceLastReboot küld az IoT Centralnak. Ez a metódus az PnpConvention osztály használatával hozza létre a javítást:

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);
}

A TargetTemperatureUpdateCallbackAsync metódus kezeli a írható célhőmérséklet tulajdonság frissítését az IoT Centralról. Ez a metódus az PnpConvention osztály használatával olvassa el a tulajdonságfrissítési üzenetet, és hozza létre a választ:

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);
}

A HandleMaxMinReportCommand metódus az IoT Centralról hívott összetevők parancsait kezeli:

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)
    {
        // ...
    }
}

Kapcsolatadatok lekérése

Amikor az oktatóanyag későbbi részében futtatja a mintaeszköz-alkalmazást, a következő konfigurációs értékekre lesz szüksége:

  • Azonosító hatóköre: Az IoT Central-alkalmazásban keresse meg az Engedélyek > eszköz kapcsolatcsoportokat. Jegyezze fel az azonosító hatókörének értékét.
  • Elsődleges csoportkulcs: Az IoT Central-alkalmazásban keresse meg az Engedélyek > eszköz kapcsolatcsoportokat > SAS-IoT-Devices. Jegyezze fel a közös hozzáférésű jogosultságkód elsődleges kulcsértékét .

Az Azure Cloud Shell használatával hozzon létre egy eszközkulcsot a lekért elsődleges csoportkulcsból:

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

Jegyezze fel a létrehozott eszközkulcsot, amelyet az oktatóanyag későbbi részében fog használni.

Feljegyzés

A minta futtatásához nem kell előre regisztrálnia az eszközt az IoT Central-alkalmazásban. A minta az IoT Central funkcióval automatikusan regisztrálja az eszközöket , amikor első alkalommal csatlakoznak.

A kód futtatása

Feljegyzés

Állítsa be a TemperatureControllert indítási projektként a kód futtatása előtt.

A mintaalkalmazás futtatása a Visual Studióban:

  1. A Megoldáskezelő válassza ki a PnpDeviceSamples > TemperatureController projektfájlt.

  2. Lépjen a Project > TemperatureController tulajdonságok > hibakeresésére. Ezután adja hozzá a következő környezeti változókat a projekthez:

    Név szerint Érték
    IOTHUB_DEVICE_Standard kiadásCURITY_TYPE DPS
    IOTHUB_DEVICE_DPS_ENDPOINT global.azure-devices-provisioning.net
    IOTHUB_DEVICE_DPS_ID_SCOPE Az azonosító hatókörének értéke, amelyről korábban feljegyzést készített.
    IOTHUB_DEVICE_DPS_DEVICE_ID sample-device-01
    IOTHUB_DEVICE_DPS_DEVICE_KEY A korábban jegyzett eszközkulcs-érték.

Most már futtathatja és hibakeresést végezhet a mintában a Visual Studióban.

Az alábbi kimenet az IoT Centralhoz való regisztrálást és csatlakozást mutatja. A minta elkezd telemetriai adatokat küldeni:

[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.

Az Azure IoT Central-alkalmazás operátoraként a következőkre van lehetőség:

  • Tekintse meg a két termosztát-összetevő által küldött telemetriát az Áttekintés lapon:

    Screenshot that shows the device overview page.

  • Az eszköz tulajdonságainak megtekintése a Névjegy lapon. Ezen az oldalon az eszközinformációs összetevő és a két termosztát-összetevő tulajdonságai láthatók:

    Screenshot that shows the device properties view.

Az eszközsablon testreszabása

Megoldásfejlesztőként testre szabhatja az IoT Central által automatikusan létrehozott eszközsablont, amikor a hőmérséklet-vezérlő eszköz csatlakoztatva van.

Felhőtulajdonság hozzáadása az eszközhöz társított ügyfélnév tárolásához:

  1. Az IoT Central-alkalmazásban lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. A Hőmérséklet-vezérlő modellben válassza a +Képesség hozzáadása lehetőséget.

  3. Adja meg az ügyfél nevét megjelenítési névként, válassza a Cloud tulajdonságot képességtípusként, bontsa ki a bejegyzést, és válassza a Sztringet sémaként. Ezután válassza a Mentés lehetőséget.

A Max-Min jelentés lekérése parancsok megjelenítésének testreszabása az IoT Central-alkalmazásban:

  1. Lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. A getMaxMinReport (termosztát1) esetében cserélje le a Max-Min jelentés lekérése elemet a Termosztát1 állapotjelentésre.

  3. A getMaxMinReport (termosztát2) esetében cserélje le a Max-Min jelentés lekérése elemet a Termosztát2 állapotjelentésre.

  4. Válassza a Mentés lehetőséget.

A Célhőmérséklet írható tulajdonságainak megjelenítésének testreszabása az IoT Central-alkalmazásban:

  1. Lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. TargetTemperature (termosztát1) esetén cserélje le a célhőmérsékletet a Célhőmérséklet (1) kifejezésre.

  3. TargetTemperature (termosztát2) esetén cserélje le a célhőmérsékletetcélhőmérsékletre (2).

  4. Válassza a Mentés lehetőséget.

A Hőmérséklet-vezérlő modell termosztát-összetevői tartalmazzák a Target Temperature írható tulajdonságot, az eszközsablon pedig az Ügyfélnév felhő tulajdonságot. Hozzon létre egy nézetet, amellyel az operátor szerkesztheti ezeket a tulajdonságokat:

  1. Válassza a Nézetek lehetőséget, majd válassza a Szerkesztés eszköz és a felhőbeli adatcsempét .

  2. Adja meg a Tulajdonságok nevet az űrlap neveként.

  3. Válassza ki a Célhőmérséklet (1), a Célhőmérséklet (2) és az Ügyfélnév tulajdonságokat. Ezután válassza a Szakasz hozzáadása lehetőséget.

  4. Mentse a módosításokat.

Screenshot that shows a view for updating property values.

Az eszközsablon közzététele

Ahhoz, hogy egy operátor láthassa és használhassa a végrehajtott testreszabásokat, közzé kell tennie az eszközsablont.

A Termosztát eszközsablonban válassza a Közzététel lehetőséget. Az eszközsablon közzététele az alkalmazáspanelen válassza a Közzététel lehetőséget.

Az operátor mostantól a Tulajdonságok nézetben frissítheti a tulajdonságértékeket, és meghívhatja a Termosztát1 állapotjelentés lekérése és a Termosztát2 állapotjelentés lekérése parancsokat az eszközparancsok oldalán:

  • A Írható tulajdonságértékek frissítése a Tulajdonságok lapon:

    Screenshot that shows updating the device properties.

  • Hívja meg a parancsokat a Parancsok lapon. Ha az állapotjelentés parancsot futtatja, a futtatás előtt válassza ki a Since paraméter dátumát és időpontját:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Láthatja, hogyan reagál az eszköz a parancsokra és a tulajdonságfrissítésekre:

[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.

Browse code

Előfeltételek

A cikk lépéseinek elvégzéséhez a következő erőforrásokra van szüksége:

  • Aktív Azure-előfizetés. Ha még nincs Azure-előfizetése, kezdés előtt hozzon létre egy ingyenes fiókot.

  • Az egyéni alkalmazássablonból létrehozott IoT Central-alkalmazás . További információ: IoT Central-alkalmazás létrehozása és az alkalmazás ismertetése.

  • Fejlesztőgép Java Standard kiadás Development Kit 8 vagy újabb verzióval. További információ: A JDK telepítése.

  • Apache Maven 3.

  • A Mintakódot tartalmazó Microsoft Azure IoT SDK for Java GitHub-adattár helyi példánya. Ezen a hivatkozáson letöltheti az adattár egy példányát: ZIP letöltése. Ezután bontsa ki a fájlt egy megfelelő helyre a helyi gépen.

A kód áttekintése

A Korábban letöltött Java Microsoft Azure IoT SDK másolatában nyissa meg az 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 fájlt egy szövegszerkesztőben.

A minta implementálja a többkomponensű Temperature Controller Digital Twin Definition Language modellt.

Amikor a mintát az IoT Centralhoz való csatlakozáshoz futtatja, az a Device Provisioning Service (DPS) használatával regisztrálja az eszközt, és létrehoz egy kapcsolati sztring. A minta lekéri a szükséges DPS-kapcsolati adatokat a parancssori környezetből.

A main módszer:

  • Meghívja initializeAndProvisionDevice a dtmi:com:example:TemperatureController;2 modellazonosító beállítását, a DPS-t az eszköz kiépítéséhez és regisztrálásához, egy DeviceClient-példány létrehozásához és az IoT Central-alkalmazáshoz való csatlakozáshoz. Az IoT Central a modellazonosítóval azonosítja vagy létrehozza az eszközhöz tartozó eszközsablont. További információ: Eszköz hozzárendelése eszközsablonhoz.
  • Parancskezelőket hoz létre a parancsokhoz és reboot parancsokhozgetMaxMinReport.
  • Tulajdonságfrissítési kezelőket hoz létre a írható targetTemperature tulajdonságokhoz.
  • Kezdeti értékeket küld az Eszközinformációs felületen lévő tulajdonságoknak, valamint az eszközmemória és a sorozatszám tulajdonságainak.
  • Elindít egy szálat, amely hőmérsékleti telemetriát küld a két termosztátból, és öt másodpercenként frissíti a maxTempSinceLastReboot tulajdonságot.
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();
}

A initializeAndProvisionDevice módszer bemutatja, hogy az eszköz hogyan használja a DPS-t az IoT Central regisztrálására és az IoT Centralhoz való csatlakozásra. A hasznos adatok tartalmazzák az IoT Central által az eszköz eszközsablonhoz való hozzárendeléséhez használt modellazonosítót:

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);
    }
}

A sendTemperatureTelemetry módszer azt mutatja be, hogy az eszköz hogyan küldi el a hőmérsékleti telemetriát egy összetevőről az IoT Centralnak. Ez a metódus az PnpConvention osztály használatával hozza létre az üzenetet:

  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);
  }

A updateMaxTemperatureSinceLastReboot metódus tulajdonságfrissítést maxTempSinceLastReboot küld egy összetevőről az IoT Centralnak. Ez a metódus az PnpConvention osztály használatával hozza létre a javítást:

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);
}

Az TargetTemperatureUpdateCallback osztály tartalmazza az onPropertyChanged IoT Central egy összetevő írható tulajdonságfrissítéseinek kezelésére szolgáló metódust. Ez a metódus az PnpConvention osztály használatával hozza létre a választ:

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.");
        }
    }
}

Az MethodCallback osztály tartalmazza az onMethodInvoked IoT Centralról hívott összetevőparancsok kezelésére szolgáló metódust:

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);
        }
    }
}

Kapcsolatadatok lekérése

Amikor az oktatóanyag későbbi részében futtatja a mintaeszköz-alkalmazást, a következő konfigurációs értékekre lesz szüksége:

  • Azonosító hatóköre: Az IoT Central-alkalmazásban keresse meg az Engedélyek > eszköz kapcsolatcsoportokat. Jegyezze fel az azonosító hatókörének értékét.
  • Elsődleges csoportkulcs: Az IoT Central-alkalmazásban keresse meg az Engedélyek > eszköz kapcsolatcsoportokat > SAS-IoT-Devices. Jegyezze fel a közös hozzáférésű jogosultságkód elsődleges kulcsértékét .

Az Azure Cloud Shell használatával hozzon létre egy eszközkulcsot a lekért elsődleges csoportkulcsból:

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

Jegyezze fel a létrehozott eszközkulcsot, amelyet az oktatóanyag későbbi részében fog használni.

Feljegyzés

A minta futtatásához nem kell előre regisztrálnia az eszközt az IoT Central-alkalmazásban. A minta az IoT Central funkcióval automatikusan regisztrálja az eszközöket , amikor első alkalommal csatlakoznak.

Windows rendszeren keresse meg a letöltött Azure IoT SDK for Java-adattár gyökérmappáját.

Futtassa a következő parancsot a mintaalkalmazás létrehozásához:

mvn install -T 2C -DskipTests

A kód futtatása

A mintaalkalmazás futtatásához nyisson meg egy parancssori környezetet, és keresse meg az azure-iot-sdk-java/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample mappát, amely az src mappát tartalmazza a TemperatureController.java mintafájllal.

Állítsa be a környezeti változókat a minta konfigurálásához. Az alábbi kódrészlet bemutatja, hogyan állíthatja be a környezeti változókat a Windows parancssorában. Ha bash-rendszerhéjat használ, cserélje le a parancsokat parancsra setexport:

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

Minta futtatása:

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

Az alábbi kimenet az IoT Centralhoz való regisztrálást és csatlakozást mutatja. A minta elkezd telemetriai adatokat küldeni:

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 [029d30d4-acbd-462d-b155-82d53ce7786c] Message Id [1b2adf93-ba81-41e4-b8c7-7c90c8b0d6a1] 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 [029d30d4-acbd-462d-b155-82d53ce7786c] Message Id [1b2adf93-ba81-41e4-b8c7-7c90c8b0d6a1] 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 [f2f9ed95-9778-44f2-b9ec-f60c84061251] Message Id [0d5abdb2-6460-414c-a10e-786ee24cacff] 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 [417d659a-7324-43fa-84eb-8a3f3d07963c] Message Id [55532cad-8a5a-489f-9aa8-8f0e5bc21541] 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 [d46a0d8a-8a18-4014-abeb-768bd9b17ad2] Message Id [780abc81-ce42-4e5f-aa80-e4785883604e] 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 [f2f9ed95-9778-44f2-b9ec-f60c84061251] Message Id [0d5abdb2-6460-414c-a10e-786ee24cacff] 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 [a77b1c02-f043-4477-b610-e31a774772c0] Message Id [2e2f6bee-c480-42cf-ac31-194118930846] 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 [bbb7e3cf-3550-4fdf-90f9-0787740f028a] Message Id [e06ac385-ae0d-46dd-857a-d9725707527a] )
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 [6dbef765-cc9a-4e72-980a-2fe5b0cd77e1] Message Id [49bbad33-09bf-417a-9d6e-299ba7b7c562] 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 [86787c32-87a5-4c49-9083-c7f2b17446a7] Message Id [0a45fa0c-a467-499d-b214-9bb5995772ba] )
2021-03-30 15:33:44.927 DEBUG TemperatureController:461 - Telemetry: Sent - {"temperature": 5.8°C} with message Id 0a45fa0c-a467-499d-b214-9bb5995772ba.

Az Azure IoT Central-alkalmazás operátoraként a következőkre van lehetőség:

  • Tekintse meg a két termosztát-összetevő által küldött telemetriát az Áttekintés lapon:

    Screenshot that shows the device overview page.

  • Az eszköz tulajdonságainak megtekintése a Névjegy lapon. Ezen az oldalon az eszközinformációs összetevő és a két termosztát-összetevő tulajdonságai láthatók:

    Screenshot that shows the device properties view.

Az eszközsablon testreszabása

Megoldásfejlesztőként testre szabhatja az IoT Central által automatikusan létrehozott eszközsablont, amikor a hőmérséklet-vezérlő eszköz csatlakoztatva van.

Felhőtulajdonság hozzáadása az eszközhöz társított ügyfélnév tárolásához:

  1. Az IoT Central-alkalmazásban lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. A Hőmérséklet-vezérlő modellben válassza a +Képesség hozzáadása lehetőséget.

  3. Adja meg az ügyfél nevét megjelenítési névként, válassza a Cloud tulajdonságot képességtípusként, bontsa ki a bejegyzést, és válassza a Sztringet sémaként. Ezután válassza a Mentés lehetőséget.

A Max-Min jelentés lekérése parancsok megjelenítésének testreszabása az IoT Central-alkalmazásban:

  1. Lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. A getMaxMinReport (termosztát1) esetében cserélje le a Max-Min jelentés lekérése elemet a Termosztát1 állapotjelentésre.

  3. A getMaxMinReport (termosztát2) esetében cserélje le a Max-Min jelentés lekérése elemet a Termosztát2 állapotjelentésre.

  4. Válassza a Mentés lehetőséget.

A Célhőmérséklet írható tulajdonságainak megjelenítésének testreszabása az IoT Central-alkalmazásban:

  1. Lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. TargetTemperature (termosztát1) esetén cserélje le a célhőmérsékletet a Célhőmérséklet (1) kifejezésre.

  3. TargetTemperature (termosztát2) esetén cserélje le a célhőmérsékletetcélhőmérsékletre (2).

  4. Válassza a Mentés lehetőséget.

A Hőmérséklet-vezérlő modell termosztát-összetevői tartalmazzák a Target Temperature írható tulajdonságot, az eszközsablon pedig az Ügyfélnév felhő tulajdonságot. Hozzon létre egy nézetet, amellyel az operátor szerkesztheti ezeket a tulajdonságokat:

  1. Válassza a Nézetek lehetőséget, majd válassza a Szerkesztés eszköz és a felhőbeli adatcsempét .

  2. Adja meg a Tulajdonságok nevet az űrlap neveként.

  3. Válassza ki a Célhőmérséklet (1), a Célhőmérséklet (2) és az Ügyfélnév tulajdonságokat. Ezután válassza a Szakasz hozzáadása lehetőséget.

  4. Mentse a módosításokat.

Screenshot that shows a view for updating property values.

Az eszközsablon közzététele

Ahhoz, hogy egy operátor láthassa és használhassa a végrehajtott testreszabásokat, közzé kell tennie az eszközsablont.

A Termosztát eszközsablonban válassza a Közzététel lehetőséget. Az eszközsablon közzététele az alkalmazáspanelen válassza a Közzététel lehetőséget.

Az operátor mostantól a Tulajdonságok nézetben frissítheti a tulajdonságértékeket, és meghívhatja a Termosztát1 állapotjelentés lekérése és a Termosztát2 állapotjelentés lekérése parancsokat az eszközparancsok oldalán:

  • A Írható tulajdonságértékek frissítése a Tulajdonságok lapon:

    Screenshot that shows updating the device properties.

  • Hívja meg a parancsokat a Parancsok lapon. Ha az állapotjelentés parancsot futtatja, a futtatás előtt válassza ki a Since paraméter dátumát és időpontját:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Láthatja, hogyan reagál az eszköz a parancsokra és a tulajdonságfrissítésekre:

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

Browse code

Előfeltételek

A cikk lépéseinek elvégzéséhez a következő erőforrásokra van szüksége:

  • Aktív Azure-előfizetés. Ha még nincs Azure-előfizetése, kezdés előtt hozzon létre egy ingyenes fiókot.

  • Az egyéni alkalmazássablonból létrehozott IoT Central-alkalmazás . További információ: IoT Central-alkalmazás létrehozása és az alkalmazás ismertetése.

  • Olyan fejlesztőgép, amely Node.js 6-os vagy újabb verzióját telepítette. A parancssorban futtatva node --version ellenőrizheti a verziót. Az oktatóanyag utasításai feltételezik, hogy a Csomópont parancsot a Windows parancssorában futtatja. A Node.js azonban számos más operációs rendszeren is használhatja.

  • A Microsoft Azure IoT SDK Node.js GitHub-adattár helyi példánya, amely tartalmazza a mintakódot. Ezen a hivatkozáson letöltheti az adattár egy példányát: ZIP letöltése. Ezután bontsa ki a fájlt egy megfelelő helyre a helyi gépen.

A kód áttekintése

A Microsoft Azure IoT SDK korábban letöltött Node.js példányában nyissa meg az azure-iot-sdk-node/device/samples/javascript/pnp_temperature_controller.js fájlt egy szövegszerkesztőben.

A minta implementálja a többkomponensű Temperature Controller Digital Twin Definition Language modellt.

Amikor a mintát az IoT Centralhoz való csatlakozáshoz futtatja, az a Device Provisioning Service (DPS) használatával regisztrálja az eszközt, és létrehoz egy kapcsolati sztring. A minta lekéri a szükséges DPS-kapcsolati adatokat a parancssori környezetből.

A main módszer:

  • Létrehoz egy client objektumot, és beállítja a dtmi:com:example:TemperatureController;2 modellazonosítót, mielőtt megnyitja a kapcsolatot. Az IoT Central a modellazonosítóval azonosítja vagy létrehozza az eszközhöz tartozó eszközsablont. További információ: Eszköz hozzárendelése eszközsablonhoz.
  • Parancskezelőket hoz létre három parancshoz.
  • Minden termosztát-összetevőhöz elindít egy hurkot, amely 5 másodpercenként küld hőmérsékleti telemetriát.
  • Elindít egy hurkot az alapértelmezett összetevőhöz, amely 6 másodpercenként küldi el a munkakészlet méret telemetriáját.
  • maxTempSinceLastReboot Minden termosztát-összetevő tulajdonságát elküldi.
  • Elküldi az eszközinformációs tulajdonságokat.
  • Írható tulajdonságok kezelőit hozza létre a három összetevőhöz.
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());
  }
}

A provisionDevice függvény bemutatja, hogy az eszköz hogyan használja a DPS-t az IoT Central regisztrálására és az IoT Centralhoz való csatlakozásra. A hasznos adat tartalmazza az IoT Central által az eszköz eszközsablonhoz való hozzárendeléséhez használt modellazonosítót:

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());
  }
}

A sendTelemetry függvény bemutatja, hogy az eszköz hogyan küldi el a hőmérsékleti telemetriát az IoT Centralnak. Az összetevők telemetriai adataihoz hozzáad egy, az összetevő nevével ellátott $.sub tulajdonságot:

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);
}

A main metódus egy segédmetódust helperCreateReportedPropertiesPatch használ a tulajdonságfrissítési üzenetek létrehozásához. Ez a metódus egy opcionális paramétert használ a tulajdonságot küldő összetevő megadásához:

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;
};

A main metódus az alábbi módszerrel kezeli az IoT Central írható tulajdonságainak frissítéseit. Figyelje meg, hogy a metódus hogyan hozza létre a választ a verzióval és az állapotkóddal:

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);
      }
    });
  });
};

A main metódus a következő metódusokat használja az IoT Central parancsainak kezelésére:

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());
  }
};

Kapcsolatadatok lekérése

Amikor az oktatóanyag későbbi részében futtatja a mintaeszköz-alkalmazást, a következő konfigurációs értékekre lesz szüksége:

  • Azonosító hatóköre: Az IoT Central-alkalmazásban keresse meg az Engedélyek > eszköz kapcsolatcsoportokat. Jegyezze fel az azonosító hatókörének értékét.
  • Elsődleges csoportkulcs: Az IoT Central-alkalmazásban keresse meg az Engedélyek > eszköz kapcsolatcsoportokat > SAS-IoT-Devices. Jegyezze fel a közös hozzáférésű jogosultságkód elsődleges kulcsértékét .

Az Azure Cloud Shell használatával hozzon létre egy eszközkulcsot a lekért elsődleges csoportkulcsból:

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

Jegyezze fel a létrehozott eszközkulcsot, amelyet az oktatóanyag későbbi részében fog használni.

Feljegyzés

A minta futtatásához nem kell előre regisztrálnia az eszközt az IoT Central-alkalmazásban. A minta az IoT Central funkcióval automatikusan regisztrálja az eszközöket , amikor első alkalommal csatlakoznak.

A kód futtatása

A mintaalkalmazás futtatásához nyisson meg egy parancssori környezetet, és keresse meg a pnp_temperature_controller.js mintafájlt tartalmazó azure-iot-sdk-node/device/samples/javascript mappát.

Állítsa be a környezeti változókat a minta konfigurálásához. Az alábbi kódrészlet bemutatja, hogyan állíthatja be a környezeti változókat a Windows parancssorában. Ha bash-rendszerhéjat használ, cserélje le a parancsokat parancsra setexport:

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

Telepítse a szükséges csomagokat:

npm install

Minta futtatása:

node pnp_temperature_controller.js

Az alábbi kimenet az IoT Centralhoz való regisztrálást és csatlakozást mutatja. A minta ezután elküldi a tulajdonságot maxTempSinceLastReboot a két termosztát-összetevőből, mielőtt elkezdi a telemetria küldését:

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

Az Azure IoT Central-alkalmazás operátoraként a következőkre van lehetőség:

  • Tekintse meg a két termosztát-összetevő által küldött telemetriát az Áttekintés lapon:

    Screenshot that shows the device overview page.

  • Az eszköz tulajdonságainak megtekintése a Névjegy lapon. Ezen az oldalon az eszközinformációs összetevő és a két termosztát-összetevő tulajdonságai láthatók:

    Screenshot that shows the device properties view.

Az eszközsablon testreszabása

Megoldásfejlesztőként testre szabhatja az IoT Central által automatikusan létrehozott eszközsablont, amikor a hőmérséklet-vezérlő eszköz csatlakoztatva van.

Felhőtulajdonság hozzáadása az eszközhöz társított ügyfélnév tárolásához:

  1. Az IoT Central-alkalmazásban lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. A Hőmérséklet-vezérlő modellben válassza a +Képesség hozzáadása lehetőséget.

  3. Adja meg az ügyfél nevét megjelenítési névként, válassza a Cloud tulajdonságot képességtípusként, bontsa ki a bejegyzést, és válassza a Sztringet sémaként. Ezután válassza a Mentés lehetőséget.

A Max-Min jelentés lekérése parancsok megjelenítésének testreszabása az IoT Central-alkalmazásban:

  1. Lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. A getMaxMinReport (termosztát1) esetében cserélje le a Max-Min jelentés lekérése elemet a Termosztát1 állapotjelentésre.

  3. A getMaxMinReport (termosztát2) esetében cserélje le a Max-Min jelentés lekérése elemet a Termosztát2 állapotjelentésre.

  4. Válassza a Mentés lehetőséget.

A Célhőmérséklet írható tulajdonságainak megjelenítésének testreszabása az IoT Central-alkalmazásban:

  1. Lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. TargetTemperature (termosztát1) esetén cserélje le a célhőmérsékletet a Célhőmérséklet (1) kifejezésre.

  3. TargetTemperature (termosztát2) esetén cserélje le a célhőmérsékletetcélhőmérsékletre (2).

  4. Válassza a Mentés lehetőséget.

A Hőmérséklet-vezérlő modell termosztát-összetevői tartalmazzák a Target Temperature írható tulajdonságot, az eszközsablon pedig az Ügyfélnév felhő tulajdonságot. Hozzon létre egy nézetet, amellyel az operátor szerkesztheti ezeket a tulajdonságokat:

  1. Válassza a Nézetek lehetőséget, majd válassza a Szerkesztés eszköz és a felhőbeli adatcsempét .

  2. Adja meg a Tulajdonságok nevet az űrlap neveként.

  3. Válassza ki a Célhőmérséklet (1), a Célhőmérséklet (2) és az Ügyfélnév tulajdonságokat. Ezután válassza a Szakasz hozzáadása lehetőséget.

  4. Mentse a módosításokat.

Screenshot that shows a view for updating property values.

Az eszközsablon közzététele

Ahhoz, hogy egy operátor láthassa és használhassa a végrehajtott testreszabásokat, közzé kell tennie az eszközsablont.

A Termosztát eszközsablonban válassza a Közzététel lehetőséget. Az eszközsablon közzététele az alkalmazáspanelen válassza a Közzététel lehetőséget.

Az operátor mostantól a Tulajdonságok nézetben frissítheti a tulajdonságértékeket, és meghívhatja a Termosztát1 állapotjelentés lekérése és a Termosztát2 állapotjelentés lekérése parancsokat az eszközparancsok oldalán:

  • A Írható tulajdonságértékek frissítése a Tulajdonságok lapon:

    Screenshot that shows updating the device properties.

  • Hívja meg a parancsokat a Parancsok lapon. Ha az állapotjelentés parancsot futtatja, a futtatás előtt válassza ki a Since paraméter dátumát és időpontját:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Láthatja, hogyan reagál az eszköz a parancsokra és a tulajdonságfrissítésekre. A getMaxMinReport parancs az thermostat2 összetevőben van, a reboot parancs az alapértelmezett összetevőben van. A targetTemperature írható tulajdonság be lett állítva az thermostat2 összetevőhöz:

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

Browse code

Előfeltételek

A cikk lépéseinek elvégzéséhez a következő erőforrásokra van szüksége:

  • Aktív Azure-előfizetés. Ha még nincs Azure-előfizetése, kezdés előtt hozzon létre egy ingyenes fiókot.

  • Az egyéni alkalmazássablonból létrehozott IoT Central-alkalmazás . További információ: IoT Central-alkalmazás létrehozása és az alkalmazás ismertetése.

  • Egy fejlesztői gép, amelyen telepítve van a Python . Tekintse meg az Azure IoT Python SDK aktuális Python-verziókövetelményeit. A parancssorban futtatva python --version ellenőrizheti a verziót. A Python számos operációs rendszerhez érhető el. Az oktatóanyag utasításai feltételezik, hogy a Python-parancsot a Windows parancssorában futtatja.

  • A Microsoft Azure IoT SDK for Python GitHub-adattár helyi példánya, amely tartalmazza a mintakódot. Ezen a hivatkozáson letöltheti az adattár egy példányát: ZIP letöltése. Ezután bontsa ki a fájlt egy megfelelő helyre a helyi gépen.

A kód áttekintése

A Korábban letöltött Microsoft Azure IoT SDK for Python példányában nyissa meg az azure-iot-sdk-python/samples/pnp/temp_controller_with_thermostats.py fájlt egy szövegszerkesztőben.

A minta implementálja a többkomponensű Temperature Controller Digital Twin Definition Language modellt.

Amikor a mintát az IoT Centralhoz való csatlakozáshoz futtatja, az a Device Provisioning Service (DPS) használatával regisztrálja az eszközt, és létrehoz egy kapcsolati sztring. A minta lekéri a szükséges DPS-kapcsolati adatokat a parancssori környezetből.

A main függvény:

  • A DPS használatával építi ki az eszközt. A kiépítési információk tartalmazzák a modell azonosítóját. Az IoT Central a modellazonosítóval azonosítja vagy létrehozza az eszközhöz tartozó eszközsablont. További információ: Eszköz hozzárendelése eszközsablonhoz.
  • Létrehoz egy Device_client objektumot, és beállítja a dtmi:com:example:TemperatureController;2 modellazonosítót, mielőtt megnyitja a kapcsolatot.
  • Kezdeti tulajdonságértékeket küld az IoT Centralnak. pnp_helper Ezzel hozza létre a javításokat.
  • Figyelőket hoz létre a parancsokhoz és reboot a getMaxMinReport parancsokhoz. Minden termosztát-összetevőnek saját getMaxMinReport parancsa van.
  • Tulajdonságfigyelőt hoz létre a írható tulajdonságfrissítések figyeléséhez.
  • Elindít egy hurkot, amely hőmérsékleti telemetriát küld a két termosztát-összetevőből, és a munkakészlet telemetriáját az alapértelmezett összetevőről 8 másodpercenként.
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())

    # ...

A provision_device függvény a DPS használatával építi ki az eszközt, és regisztrálja az IoT Centralban. A függvény tartalmazza az eszközmodell azonosítóját, amelyet az IoT Central egy eszközsablonhoz való hozzárendeléséhez használ a kiépítés hasznos adatai között:

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()

A execute_command_listener függvény kezeli a parancskéréseket, futtatja a max_min_handler függvényt, amikor az eszköz megkapja a getMaxMinReport termosztát összetevőinek parancsát, és a reboot_handler függvényt, amikor az eszköz megkapja a reboot parancsot. A modullal pnp_helper hozza létre a választ:

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))

A async def execute_property_listener leírók írható tulajdonságfrissítéseket, például targetTemperature a termosztát összetevőit kezelik, és JSON-választ generálnak. A modullal pnp_helper hozza létre a választ:

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)

A send_telemetry_from_temp_controller függvény elküldi a telemetriai üzeneteket a termosztát összetevőiről az IoT Centralnak. A modul használatával pnp_helper hozza létre az üzeneteket:

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)

Kapcsolatadatok lekérése

Amikor az oktatóanyag későbbi részében futtatja a mintaeszköz-alkalmazást, a következő konfigurációs értékekre lesz szüksége:

  • Azonosító hatóköre: Az IoT Central-alkalmazásban keresse meg az Engedélyek > eszköz kapcsolatcsoportokat. Jegyezze fel az azonosító hatókörének értékét.
  • Elsődleges csoportkulcs: Az IoT Central-alkalmazásban keresse meg az Engedélyek > eszköz kapcsolatcsoportokat > SAS-IoT-Devices. Jegyezze fel a közös hozzáférésű jogosultságkód elsődleges kulcsértékét .

Az Azure Cloud Shell használatával hozzon létre egy eszközkulcsot a lekért elsődleges csoportkulcsból:

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

Jegyezze fel a létrehozott eszközkulcsot, amelyet az oktatóanyag későbbi részében fog használni.

Feljegyzés

A minta futtatásához nem kell előre regisztrálnia az eszközt az IoT Central-alkalmazásban. A minta az IoT Central funkcióval automatikusan regisztrálja az eszközöket , amikor első alkalommal csatlakoznak.

A kód futtatása

A mintaalkalmazás futtatásához nyisson meg egy parancssori környezetet, és keresse meg az azure-iot-sdk-python-2/samples/pnp mappát, amely a temp_controller_with_thermostats.py mintafájlt tartalmazza.

Állítsa be a környezeti változókat a minta konfigurálásához. Az alábbi kódrészlet bemutatja, hogyan állíthatja be a környezeti változókat a Windows parancssorában. Ha bash-rendszerhéjat használ, cserélje le a parancsokat parancsra setexport:

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

Telepítse a szükséges csomagokat:

pip install azure-iot-device

Minta futtatása:

python temp_controller_with_thermostats.py

Az alábbi kimenet az IoT Centralhoz való regisztrálást és csatlakozást mutatja. A minta elküldi a maxTempSinceLastReboot tulajdonságokat a két termosztát-összetevőből, mielőtt elkezdi a telemetriai adatokat:

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}

Az Azure IoT Central-alkalmazás operátoraként a következőkre van lehetőség:

  • Tekintse meg a két termosztát-összetevő által küldött telemetriát az Áttekintés lapon:

    Screenshot that shows the device overview page.

  • Az eszköz tulajdonságainak megtekintése a Névjegy lapon. Ezen az oldalon az eszközinformációs összetevő és a két termosztát-összetevő tulajdonságai láthatók:

    Screenshot that shows the device properties view.

Az eszközsablon testreszabása

Megoldásfejlesztőként testre szabhatja az IoT Central által automatikusan létrehozott eszközsablont, amikor a hőmérséklet-vezérlő eszköz csatlakoztatva van.

Felhőtulajdonság hozzáadása az eszközhöz társított ügyfélnév tárolásához:

  1. Az IoT Central-alkalmazásban lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. A Hőmérséklet-vezérlő modellben válassza a +Képesség hozzáadása lehetőséget.

  3. Adja meg az ügyfél nevét megjelenítési névként, válassza a Cloud tulajdonságot képességtípusként, bontsa ki a bejegyzést, és válassza a Sztringet sémaként. Ezután válassza a Mentés lehetőséget.

A Max-Min jelentés lekérése parancsok megjelenítésének testreszabása az IoT Central-alkalmazásban:

  1. Lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. A getMaxMinReport (termosztát1) esetében cserélje le a Max-Min jelentés lekérése elemet a Termosztát1 állapotjelentésre.

  3. A getMaxMinReport (termosztát2) esetében cserélje le a Max-Min jelentés lekérése elemet a Termosztát2 állapotjelentésre.

  4. Válassza a Mentés lehetőséget.

A Célhőmérséklet írható tulajdonságainak megjelenítésének testreszabása az IoT Central-alkalmazásban:

  1. Lépjen a Hőmérséklet-vezérlő eszközsablonjára az Eszközsablonok lapon.

  2. TargetTemperature (termosztát1) esetén cserélje le a célhőmérsékletet a Célhőmérséklet (1) kifejezésre.

  3. TargetTemperature (termosztát2) esetén cserélje le a célhőmérsékletetcélhőmérsékletre (2).

  4. Válassza a Mentés lehetőséget.

A Hőmérséklet-vezérlő modell termosztát-összetevői tartalmazzák a Target Temperature írható tulajdonságot, az eszközsablon pedig az Ügyfélnév felhő tulajdonságot. Hozzon létre egy nézetet, amellyel az operátor szerkesztheti ezeket a tulajdonságokat:

  1. Válassza a Nézetek lehetőséget, majd válassza a Szerkesztés eszköz és a felhőbeli adatcsempét .

  2. Adja meg a Tulajdonságok nevet az űrlap neveként.

  3. Válassza ki a Célhőmérséklet (1), a Célhőmérséklet (2) és az Ügyfélnév tulajdonságokat. Ezután válassza a Szakasz hozzáadása lehetőséget.

  4. Mentse a módosításokat.

Screenshot that shows a view for updating property values.

Az eszközsablon közzététele

Ahhoz, hogy egy operátor láthassa és használhassa a végrehajtott testreszabásokat, közzé kell tennie az eszközsablont.

A Termosztát eszközsablonban válassza a Közzététel lehetőséget. Az eszközsablon közzététele az alkalmazáspanelen válassza a Közzététel lehetőséget.

Az operátor mostantól a Tulajdonságok nézetben frissítheti a tulajdonságértékeket, és meghívhatja a Termosztát1 állapotjelentés lekérése és a Termosztát2 állapotjelentés lekérése parancsokat az eszközparancsok oldalán:

  • A Írható tulajdonságértékek frissítése a Tulajdonságok lapon:

    Screenshot that shows updating the device properties.

  • Hívja meg a parancsokat a Parancsok lapon. Ha az állapotjelentés parancsot futtatja, a futtatás előtt válassza ki a Since paraméter dátumát és időpontját:

    Screenshot that shows calling a command.

    Screenshot that shows a command response.

Láthatja, hogyan reagál az eszköz a parancsokra és a tulajdonságfrissítésekre:

{'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"}

Nyers adatok megtekintése

A Nyers adat nézet használatával megvizsgálhatja, hogy az eszköz milyen nyers adatokat küld az IoT Centralnak:

Screenshot that shows the raw data view.

Ebben a nézetben kiválaszthatja a megjelenítendő oszlopokat, és beállíthatja a megtekinteni kívánt időtartományt. A Nemmodellezett adat oszlop olyan eszközadatokat jelenít meg, amelyek nem felelnek meg az eszközsablon egyik tulajdonság- vagy telemetriadefiníciójának sem.

Az erőforrások eltávolítása

Ha nem tervezi további IoT Central-rövid útmutatók vagy oktatóanyagok elvégzését, törölheti az IoT Central-alkalmazást:

  1. Az IoT Central-alkalmazásban lépjen az Alkalmazáskezelés elemre>.
  2. Válassza a Törlés lehetőséget, majd erősítse meg a műveletet.

Következő lépések

Ha szeretné folytatni az IoT Central-oktatóanyagok készletét, és többet szeretne megtudni az IoT Central-megoldások készítéséről, tekintse meg a következőket: