使用所需属性配置设备

简介

IoT 中心设备孪生入门介绍了如何使用标记来设置设备元数据, 并说明了如何使用报告的属性通过设备应用接收设备条件,然后使用类似 SQL 的语言查询此信息。

本教程介绍如何使用设备孪生的所需属性和报告的属性来远程配置设备应用。 可以使用设备孪生中报告的属性和所需属性通过多个步骤来配置设备应用程序,并可在所有设备中查看此操作的状态。 有关在配置设备时所需角色的详细信息,请参阅使用 IoT 中心进行设备管理概述

Note

本文中所述的功能仅可在 IoT 中心的标准层中使用。 有关基本和标准 IoT 中心层的详细信息,请参阅如何选择合适的 IoT 中心层

简而言之,使用设备孪生时,解决方案后端可以为托管的设备指定所需配置,而不需要发送特定的命令。 设备负责设置更新其配置状态的最佳方式(对于特定设备条件影响即时执行特定命令功能的 IoT 方案而言十分重要),同时持续报告更新过程的当前状态和潜在的错误条件。 此模式可用来管理大量设备,因为它让解决方案后端能够跨所有设备完全掌握配置流程状态。

Tip

在以更具交互性的方式控制设备的方案中(例如,通过用户控制的应用打开风扇),请考虑使用直接方法

在本教程中,解决方案后端更改目标设备的遥测配置,这样一来,设备应用就可以应用配置更新。 例如,某项配置更新可能要求软件模块重启,这在本教程中以简单的延迟进行模拟。

解决方案后端采用以下方式将配置存储在设备孪生的所需属性中:

    {
        ...
        "properties": {
            ...
            "desired": {
                "telemetryConfig": {
                    "configId": "{id of the configuration}",
                    "sendFrequency": "{config}"
                }
            }
            ...
        }
        ...
    }

由于配置可能是复杂对象,因此会为它们分配唯一 ID(哈希值或 GUID)。

设备应用以镜像报告属性中的所需属性 telemetryConfig 的形式报告其当前配置:

    {
        "properties": {
            ...
            "reported": {
                "telemetryConfig": {
                    "configId": "{id of the current configuration}",
                    "sendFrequency": "{current configuration}",
                    "status": "Success",
                }
            }
            ...
        }
    }

请注意,报告的 telemetryConfig 具有 status 这个其他属性,该属性用于报告配置更新过程的状态。

收到新的所需配置时,设备应用通过更改以下状态报告挂起的配置:

    {
        "properties": {
            ...
            "reported": {
                "telemetryConfig": {
                    "configId": "{id of the current configuration}",
                    "sendFrequency": "{current configuration}",
                    "status": "Pending",
                    "pendingConfig": {
                        "configId": "{id of the pending configuration}",
                        "sendFrequency": "{pending configuration}"
                    }
                }
            }
            ...
        }
    }

然后,在稍后的某个时间,设备应用会通过更新属性报告此操作是成功还是失败。 解决方案后端可以跨所有设备随时查询配置流程的状态。

本教程演示如何:

  • 创建一个模拟设备应用,用于接收来自解决方案后端的配置更新,以及将多个更新作为配置更新过程的报告属性进行报告。
  • 创建一个后端应用,用于更新设备的所需配置,并查询配置更新流程。

在本教程结束时,会创建两个控制台应用:

  • SimulateDeviceConfiguration.js,一个模拟设备应用,它等待所需配置更新并报告模拟配置更新过程的状态。
  • SetDesiredConfigurationAndQuery,一个 .NET 后端应用,用于在设备上设置所需配置并查询配置更新过程。

Note

Azure IoT SDK 文章介绍了可用于构建设备和后端应用的 Azure IoT SDK。

若要完成本教程,需要满足以下条件:

  • Visual Studio 2015 或 Visual Studio 2017。

  • Node.js 版本 4.0.x 或更高版本。

  • 有效的 Azure 帐户。 如果没有帐户,可以创建一个试用帐户,只需几分钟即可完成。

如果已按照 设备孪生入门 教程执行了操作,则现在已有一个 IoT 中心和一个名为 myDeviceId 的设备标识。 在这种情况下,可以跳到 创建模拟设备应用 部分。

创建 IoT 中心

创建模拟设备应用要连接到的 IoT 中心。 以下步骤说明如何使用 Azure 门户来完成此任务。

  1. 登录到 Azure 门户

  2. 选择“新建” > “物联网” > “IoT 中心”。

    Azure 门户跳转栏

  3. 在“IoT 中心”窗格中,输入 IoT 中心的以下信息:

    • 名称:创建 IoT 中心的名称。 如果输入的名称有效,则显示一个绿色复选标记。

    Important

    IoT 中心将公开为 DNS 终结点,因此,命名时请务必避免包含任何敏感信息。

    • 定价和缩放级别:对于本教程,请选择F1 - 免费级别。 有关详细信息,请参阅定价和缩放层

    • 资源组:创建用于托管 IoT 中心的资源组,或使用现有的资源组。 有关详细信息,请参阅使用资源组管理 Azure 资源

    • 位置:选择最近的位置。

    • 固定仪表板:选中此选项可以方便地从仪表板访问 IoT 中心。

      IoT 中心窗口

  4. 单击“创建”。 创建 IoT 中心可能需要数分钟的时间。 可在“通知”窗格中监视进度。

  5. 新的 IoT 中心就绪以后,请在 Azure 门户中单击其磁贴,打开其属性窗口。 创建 IoT 中心以后,即可找到将设备和应用程序连接到 IoT 中心时需要使用的重要信息。 记下“主机名”,并单击“共享访问策略”。

    新建 IoT 中心窗口

  6. 在“共享访问策略”中,单击“iothubowner”策略,然后记下“iothubowner”窗口中的 IoT 中心连接字符串。 有关详细信息,请参阅“IoT 中心开发人员指南”中的访问控制

    共享访问策略

创建设备标识

本部分使用名为 iothub-explorer 的 Node.js 工具为本教程创建设备标识。 设备 ID 区分大小写。

  1. 在命令行环境中运行以下命令:

    npm install -g iothub-explorer@latest

  2. 然后,运行以下命令登录到中心。 将 {iot hub connection string} 替换为前面复制的 IoT 中心连接字符串:

    iothub-explorer login "{iot hub connection string}"

  3. 最后,以下使用命令创建名为 myDeviceId 的新设备标识:

    iothub-explorer create myDeviceId --connection-string

    Important

    收集的日志中可能会显示设备 ID 用于客户支持和故障排除,因此,在为日志命名时,请务必避免包含任何敏感信息。

记下结果中的设备连接字符串。 设备应用使用此设备连接字符串以设备身份连接到 IoT 中心。

若要以编程方式创建设备标识,请参阅 IoT 中心入门

创建模拟设备应用

在此部分,用户需创建一个 Node.js 控制台应用,该应用可作为 myDeviceId连接到中心并等待所需配置更新,并针对模拟配置更新过程报告更新。

  1. 新建名为 simulatedeviceconfiguration的空文件夹。 在命令提示符下的 simulatedeviceconfiguration 文件夹中,使用以下命令创建新的 package.json 文件。 接受所有默认值。

    npm init
    
  2. simulatedeviceconfiguration 文件夹的命令提示符处,运行下述命令以安装 azure-iot-deviceazure-iot-device-mqtt 包:

    npm install azure-iot-device azure-iot-device-mqtt --save
    
  3. 使用文本编辑器,在 simulatedeviceconfiguration 文件夹中创建新的 SimulateDeviceConfiguration.js 文件。
  4. 将以下代码添加到 SimulateDeviceConfiguration.js 文件,并将 {device connection string} 占位符替换为创建 myDeviceId 设备标识时复制的设备连接字符串:

     'use strict';
     var Client = require('azure-iot-device').Client;
     var Protocol = require('azure-iot-device-mqtt').Mqtt;
    
     var connectionString = '{device connection string}';
     var client = Client.fromConnectionString(connectionString, Protocol);
    
     client.open(function(err) {
         if (err) {
             console.error('could not open IotHub client');
         } else {
             client.getTwin(function(err, twin) {
                 if (err) {
                     console.error('could not get twin');
                 } else {
                     console.log('retrieved device twin');
                     twin.properties.reported.telemetryConfig = {
                         configId: "0",
                         sendFrequency: "24h"
                     }
                     twin.on('properties.desired', function(desiredChange) {
                         console.log("received change: "+JSON.stringify(desiredChange));
                         var currentTelemetryConfig = twin.properties.reported.telemetryConfig;
                         if (desiredChange.telemetryConfig &&desiredChange.telemetryConfig.configId !== currentTelemetryConfig.configId) {
                             initConfigChange(twin);
                         }
                     });
                 }
             });
         }
     });
    

    客户端对象公开从设备与设备孪生进行交互所需的所有方法。 此代码将初始化 Client 对象,检索 myDeviceId 的设备孪生,并在所需属性上附加用于更新的处理程序。 该处理程序通过比较 configId 来验证是否存在实际配置更改请求,并调用启动配置更改的方法。

    请注意,为了简单起见,此代码使用初始配置的硬编码默认值。 实际的应用可能会从本地存储加载该配置。

    Important

    连接设备时,始终发出一次所需的属性更改事件。 执行任何操作之前,请确保检查所需属性是否进行过实际更改。

  5. client.open() 调用前添加以下方法:

    var initConfigChange = function(twin) {
        var currentTelemetryConfig = twin.properties.reported.telemetryConfig;
        currentTelemetryConfig.pendingConfig = twin.properties.desired.telemetryConfig;
        currentTelemetryConfig.status = "Pending";
    
        var patch = {
        telemetryConfig: currentTelemetryConfig
        };
        twin.properties.reported.update(patch, function(err) {
            if (err) {
                console.log('Could not report properties');
            } else {
                console.log('Reported pending config change: ' + JSON.stringify(patch));
                setTimeout(function() {completeConfigChange(twin);}, 60000);
            }
        });
    }
    
    var completeConfigChange =  function(twin) {
        var currentTelemetryConfig = twin.properties.reported.telemetryConfig;
        currentTelemetryConfig.configId = currentTelemetryConfig.pendingConfig.configId;
        currentTelemetryConfig.sendFrequency = currentTelemetryConfig.pendingConfig.sendFrequency;
        currentTelemetryConfig.status = "Success";
        delete currentTelemetryConfig.pendingConfig;
    
        var patch = {
            telemetryConfig: currentTelemetryConfig
        };
        patch.telemetryConfig.pendingConfig = null;
    
        twin.properties.reported.update(patch, function(err) {
            if (err) {
                console.error('Error reporting properties: ' + err);
            } else {
                console.log('Reported completed config change: ' + JSON.stringify(patch));
            }
        });
    };
    

initConfigChange 方法使用配置更新请求更新本地设备孪生对象的报告属性,并将状态设置为“等待中”*,并更新服务的设备孪生。 成功更新设备孪生后,它会模拟在执行 *completeConfigChange 期间终止的长时间运行的进程。 此方法更新本地报告属性,将状态设置为“成功”并删除 pendingConfig 对象。 然后,它会更新服务的设备孪生。

请注意,为了节省带宽,仅通过指定要修改的属性(在上述代码中名为 patch)而不是替换整个文档来更新报告属性。

> [!NOTE]
> 本教程不模拟并发配置更新的任何行为。 某些配置更新进程在更新运行过程中可能能够适应目标配置的更改,某些配置更新进程则可能必须将它们排队,某些配置更新进程会拒绝它们并显示错误情况。 请确保考虑特定配置过程所需的行为,并在开始配置更改之前添加相应的逻辑。
> 
> 
  1. 运行设备应用:

    node SimulateDeviceConfiguration.js
    

    此时会显示消息 retrieved device twin。 使应用保持运行状态。

创建服务应用

在本节中,用户需创建一个 .NET 控制台应用,以便通过新的遥测配置对象在与 myDeviceId 关联的设备孪生上更新 所需属性 。 该应用随后会查询存储在 IoT 中心的设备孪生,并显示设备的所需配置与报告配置之间的差异。

  1. 在 Visual Studio 中,使用“ 控制台应用程序 ”项目模板将 Visual C# Windows 经典桌面项目添加到当前解决方案。 SetDesiredConfigurationAndQuery

    新的 Visual C# Windows 经典桌面项目

  2. 在“解决方案资源管理器”中,右键单击“SetDesiredConfigurationAndQuery”项目,并单击“管理 NuGet 包”。

  3. 在“NuGet 包管理器”窗口中,选择“浏览”,搜索 microsoft.azure.devices,选择“安装”以安装 Microsoft.Azure.Devices 包,并接受使用条款。 此过程会下载、安装 Azure IoT 服务 SDK NuGet 包及其依赖项并添加对它的引用。

    “NuGet 包管理器”窗口

  4. Program.cs 文件顶部添加以下 using 语句:

    using Microsoft.Azure.Devices;
    using System.Threading;
    using Newtonsoft.Json;
    
  5. 将以下字段添加到 Program 类。 将占位符值替换为在上一部分中为中心创建的 IoT 中心连接字符串。

    static RegistryManager registryManager;
    static string connectionString = "{iot hub connection string}";
    
  6. 将以下方法添加到 Program 类:

    static private async Task SetDesiredConfigurationAndQuery()
    {
        var twin = await registryManager.GetTwinAsync("myDeviceId");
        var patch = new {
                properties = new {
                    desired = new {
                        telemetryConfig = new {
                            configId = Guid.NewGuid().ToString(),
                            sendFrequency = "5m"
                        }
                    }
                }
            };
    
        await registryManager.UpdateTwinAsync(twin.DeviceId, JsonConvert.SerializeObject(patch), twin.ETag);
        Console.WriteLine("Updated desired configuration");
    
        try
        {
         while (true)
         {
            var query = registryManager.CreateQuery("SELECT * FROM devices WHERE deviceId = 'myDeviceId'");
            var results = await query.GetNextAsTwinAsync();
            foreach (var result in results)
            {
                Console.WriteLine("Config report for: {0}", result.DeviceId);
                Console.WriteLine("Desired telemetryConfig: {0}", JsonConvert.SerializeObject(result.Properties.Desired["telemetryConfig"], Formatting.Indented));
                Console.WriteLine("Reported telemetryConfig: {0}", JsonConvert.SerializeObject(result.Properties.Reported["telemetryConfig"], Formatting.Indented));
                Console.WriteLine();
            }
            Thread.Sleep(10000);
         }
       }
       catch (Exception ex)
       {
            Console.WriteLine($"Exception: {ex.Message}");
       }
    }
    

    Registry 对象公开从服务与设备孪生进行交互所需的所有方法。 此代码将初始化 Registry 对象,然后检索 myDeviceId 的设备孪生,并使用新的遥测配置对象更新其所需属性。 然后,该代码会每隔 10 秒钟查询一次存储在 IoT 中心的设备孪生,并打印所需遥测配置和报告遥测配置。 若要了解如何在所有设备中生成丰富的报告,请参阅 IoT 中心查询语言

    Important

    为进行说明,此应用程序每 10 秒查询 IoT 中心一次。 使用查询跨多个设备生成面向用户的报表,而不检测更改。 如果解决方案需要设备事件的实时通知,请使用孪生通知

  7. 最后,在 Main 方法中添加以下行:

    registryManager = RegistryManager.CreateFromConnectionString(connectionString);
    SetDesiredConfigurationAndQuery();
    Console.WriteLine("Press any key to quit.");
    Console.ReadLine();
    
  8. 在“解决方案资源管理器”中,打开“设置启动项目...”,并确保 SetDesiredConfigurationAndQuery 项目的“操作”为“启动”。 生成解决方案。

  9. SimulateDeviceConfiguration.js 运行时,使用 F5 从 Visual Studio 运行 .NET 应用程序,应看到报告的配置以五分钟(而不是 24 小时)的新活动发送频率从 Success 更改为 Pending,然后再次更改为 Success

    已成功配置设备

    Important

    设备报告操作与查询结果之间最多存在一分钟的延迟。这是为了使查询基础结构可以采用非常大的规模来工作。若要检索单个设备孪生的一致视图,请使用 Registry 类中的 getDeviceTwin 方法。

后续步骤

在本教程中,已从解决方案后端将所需配置设置为“所需属性”,此外还编写了一个设备应用以检测该更改并模拟多步骤更新过程(通过报告属性报告其状态)。

充分利用以下资源:

  • 通过 Get started with IoT Hub (IoT 中心入门)教程学习如何从设备发送遥测;
  • 关于对大型设备集进行计划或执行操作,请参阅 计划和广播作业 教程。
  • 通过使用直接方法教程学习如何以交互方式控制设备(例如从用户控制的应用打开风扇)。