设备孪生入门 (Java)

设备孪生是存储设备状态信息(元数据、配置和条件)的 JSON 文档。 IoT 中心为连接到它的每台设备保留一个设备孪生。

Note

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

使用设备孪生可以:

  • 存储来自解决方案后端的设备元数据。
  • 通过设备应用报告当前状态信息,例如可用功能和条件(例如,使用的连接方法)。
  • 同步设备应用和后端应用之间的长时间运行的工作流的状态(例如固件和配置更新)。
  • 查询设备的元数据、配置或状态。

设备孪生旨在执行同步以及查询设备的配置和条件。 了解设备孪生中提供了有关何时使用设备孪生的详细信息。

设备孪生存储在 IoT 中心内,其中包含:

  • 标记,仅可由解决方案后端访问的设备元数据;
  • 所需属性,可以由解决方案后端修改以及由设备应用观察的 JSON 对象;以及
  • 报告属性,可由设备应用修改以及由解决方案后端读取的 JSON 对象。 标记和属性不能包含数组,但可以嵌套对象。

此外,解决方案后端可以根据上述所有数据查询设备孪生。 有关设备孪生的详细信息,请参阅了解设备孪生,有关查询的参考,请参阅 IoT 中心查询语言

本教程演示如何:

  • 创建将标记添加到设备孪生的后端应用,以及将其连接通道作为设备孪生上的报告属性进行报告的模拟设备应用。
  • 使用标记上的筛选器和之前创建的属性通过后端应用查询设备。

在本教程中,你将创建两个 Java 控制台应用:

  • add-tags-query:一个 Java 后端应用,用于添加标记并查询设备孪生。
  • simulated-device:Java 设备应用,它连接到 IoT 中心,并使用报告的属性报告其连接状态。

Note

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

要完成本教程,需要:

创建 IoT 中心

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

  1. 登录到 Azure 门户

  2. 选择“创建资源” > “物联网” > “IoT 中心”。

    Azure 门户跳转栏

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

    • 订阅:选择需要将其用于创建此 IoT 中心的订阅。

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

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

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

    Important

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

    IoT 中心基本信息窗口

  4. 选择“下一步: 大小和规模”,以便继续创建 IoT 中心。

  5. 选择“定价和缩放层”。 就本文来说,请选择“F1 - 免费”层(前提是此层在订阅上仍然可用)。 有关详细信息,请参阅定价和缩放层

    IoT 中心大小和规模窗口

  6. 选择“查看 + 创建”。

  7. 查看 IoT 中心信息,然后单击“创建”。 创建 IoT 中心可能需要数分钟的时间。 可在“通知”窗格中监视进度。

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

  9. 在“共享访问策略”中,选择 iothubowner 策略。 复制 IoT 中心连接字符串 ---主密钥供以后使用。 有关详细信息,请参阅“IoT 中心开发人员指南”中的访问控制

    共享访问策略

创建设备标识

在本部分中,将使用 Azure 门户在 IoT 中心的标识注册表中创建设备标识。 设备无法连接到 IoT 中心,除非它在标识注册表中具有条目。 有关详细信息,请参阅 IoT 中心开发人员指南的“标识注册表”部分。 使用门户中的“IoT 设备”面板为设备生成唯一设备 ID 和密钥,以用于在 IoT 中心中标识它本身。 设备 ID 区分大小写。

  1. 登录到 Azure 门户

  2. 选择“所有资源”,并查找 IoT 中心资源。

  3. 打开 IoT 中心资源后,单击“IoT 设备”工具,并单击顶部的“添加”。

    在门户中创建设备标识

  4. 提供新设备的名称(例如 myDeviceId),然后单击“保存”。 此操作会为 IoT 中心创建新设备标识。

    Important

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

    添加新设备

  5. 在设备列表中,单击新创建的设备,复制“连接字符串---主键”供以后使用。

    设备连接字符串

Note

IoT 中心标识注册表仅存储用于实现 IoT 中心安全访问的设备标识。 它存储设备 ID 和密钥作为安全凭据,以及启用/禁用标志让你禁用对单个设备的访问。 如果应用程序需要存储其他特定于设备的元数据,则应使用特定于应用程序的存储。 有关详细信息,请参阅 IoT 中心开发人员指南

创建服务应用

本部分将创建一个 Java 应用,用于将位置元数据作为标记添加到 IoT 中心内与 myDeviceId 关联的设备孪生。 该应用首先在 IoT 中心查询设备,然后查询报告已建立移动电话网络连接的设备。

  1. 在开发计算机上,创建名为 iot-java-twin-getstarted 的空文件夹。

  2. iot-java-twin-getstarted 文件夹中,通过命令提示符使用以下命令创建名为 add-tags-query 的 Maven 项目。 请注意,这是一条很长的命令:

    mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=add-tags-query -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

  3. 在命令提示符下,导航到 add-tags-query 文件夹。

  4. 使用文本编辑器打开 add-tags-query 文件夹中的 pom.xml 文件,在 dependencies 节点中添加以下依赖项。 通过此依赖项可以使用应用中的 iot-service-client 包来与 IoT 中心通信:

    <dependency>
      <groupId>com.microsoft.azure.sdk.iot</groupId>
      <artifactId>iot-service-client</artifactId>
      <version>1.7.23</version>
      <type>jar</type>
    </dependency>
    

    Note

    可以使用 Maven 搜索检查是否有最新版本的 iot-service-client

  5. dependencies 节点后添加以下 build 节点。 此配置指示 Maven 使用 Java 1.8 来生成应用:

    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.3</version>
          <configuration>
            <source>1.8</source>
            <target>1.8</target>
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  6. 保存并关闭 pom.xml 文件。

  7. 使用文本编辑器打开 add-tags-query\src\main\java\com\mycompany\app\App.java 文件。

  8. 在该文件中添加以下 import 语句:

    import com.microsoft.azure.sdk.iot.service.devicetwin.*;
    import com.microsoft.azure.sdk.iot.service.exceptions.IotHubException;
    
    import java.io.IOException;
    import java.util.HashSet;
    import java.util.Set;
    
  9. 将以下类级变量添加到 App 类。 将 {youriothubconnectionstring} 替换为在“创建 IoT 中心”部分记下的 IoT 中心连接字符串:

    public static final String iotHubConnectionString = "{youriothubconnectionstring}";
    public static final String deviceId = "myDeviceId";
    
    public static final String region = "US";
    public static final String plant = "Redmond43";
    
  10. 更新 main 方法签名,以包含以下 throws 子句:

    public static void main( String[] args ) throws IOException
    
  11. 将以下代码添加到 main 方法,以创建 DeviceTwinDeviceTwinDevice 对象。 DeviceTwin 对象处理与 IoT 中心之间的通信。 DeviceTwinDevice 对象使用设备孪生的属性和标记来表示设备孪生:

    // Get the DeviceTwin and DeviceTwinDevice objects
    DeviceTwin twinClient = DeviceTwin.createFromConnectionString(iotHubConnectionString);
    DeviceTwinDevice device = new DeviceTwinDevice(deviceId);
    
  12. 将以下 try/catch 块添加到 main 方法:

    try {
      // Code goes here
    } catch (IotHubException e) {
      System.out.println(e.getMessage());
    } catch (IOException e) {
      System.out.println(e.getMessage());
    }
    
  13. 若要更新设备孪生中的 regionplant 设备孪生标记,请在 try 块中添加以下代码:

    // Get the device twin from IoT Hub
    System.out.println("Device twin before update:");
    twinClient.getTwin(device);
    System.out.println(device);
    
    // Update device twin tags if they are different
    // from the existing values
    String currentTags = device.tagsToString();
    if ((!currentTags.contains("region=" + region) && !currentTags.contains("plant=" + plant))) {
      // Create the tags and attach them to the DeviceTwinDevice object
      Set<Pair> tags = new HashSet<Pair>();
      tags.add(new Pair("region", region));
      tags.add(new Pair("plant", plant));
      device.setTags(tags);
    
      // Update the device twin in IoT Hub
      System.out.println("Updating device twin");
      twinClient.updateTwin(device);
    }
    
    // Retrieve the device twin with the tag values from IoT Hub
    System.out.println("Device twin after update:");
    twinClient.getTwin(device);
    System.out.println(device);
    
  14. 若要在 IoT 中心查询设备孪生,请将以下代码添加到 try 块(添加到上一步骤中添加的代码后面)。 该代码运行两个查询。 每个查询最多返回 100 个设备:

    // Query the device twins in IoT Hub
    System.out.println("Devices in Redmond:");
    
    // Construct the query
    SqlQuery sqlQuery = SqlQuery.createSqlQuery("*", SqlQuery.FromType.DEVICES, "tags.plant='Redmond43'", null);
    
    // Run the query, returning a maximum of 100 devices
    Query twinQuery = twinClient.queryTwin(sqlQuery.getQuery(), 100);
    while (twinClient.hasNextDeviceTwin(twinQuery)) {
      DeviceTwinDevice d = twinClient.getNextDeviceTwin(twinQuery);
      System.out.println(d.getDeviceId());
    }
    
    System.out.println("Devices in Redmond using a cellular network:");
    
    // Construct the query
    sqlQuery = SqlQuery.createSqlQuery("*", SqlQuery.FromType.DEVICES, "tags.plant='Redmond43' AND properties.reported.connectivityType = 'cellular'", null);
    
    // Run the query, returning a maximum of 100 devices
    twinQuery = twinClient.queryTwin(sqlQuery.getQuery(), 3);
    while (twinClient.hasNextDeviceTwin(twinQuery)) {
      DeviceTwinDevice d = twinClient.getNextDeviceTwin(twinQuery);
      System.out.println(d.getDeviceId());
    }
    
  15. 保存并关闭 add-tags-query\src\main\java\com\mycompany\app\App.java 文件

  16. 生成 add-tags-query 应用并更正任何错误。 在命令提示符下,导航到 add-tags-query 文件夹并运行以下命令:

    mvn clean package -DskipTests

创建设备应用

本部分创建一个 Java 控制台应用,用于设置要发送到 IoT 中心的报告属性值。

  1. 在命令提示符下使用以下命令,在 iot-java-twin-getstarted 文件夹中创建名为 simulated-device 的 Maven 项目。 请注意,这是一条很长的命令:

    mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=simulated-device -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

  2. 在命令提示符下,导航到 simulated-device 文件夹。

  3. 使用文本编辑器打开 simulated-device 文件夹中的 pom.xml 文件,在 dependencies 节点中添加以下依赖项。 通过此依赖项可以使用应用中的 iot-device-client 包来与 IoT 中心进行通信:

    <dependency>
      <groupId>com.microsoft.azure.sdk.iot</groupId>
      <artifactId>iot-device-client</artifactId>
      <version>1.3.32</version>
    </dependency>
    

    Note

    可以使用 Maven 搜索检查是否有最新版本的 iot-device-client

  4. dependencies 节点后添加以下 build 节点。 此配置指示 Maven 使用 Java 1.8 来生成应用:

    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.3</version>
          <configuration>
            <source>1.8</source>
            <target>1.8</target>
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  5. 保存并关闭 pom.xml 文件。

  6. 使用文本编辑器打开 simulated-device\src\main\java\com\mycompany\app\App.java 文件。

  7. 在该文件中添加以下 import 语句:

    import com.microsoft.azure.sdk.iot.device.*;
    import com.microsoft.azure.sdk.iot.device.DeviceTwin.*;
    
    import java.io.IOException;
    import java.net.URISyntaxException;
    import java.util.Scanner;
    
  8. 将以下类级变量添加到 App 类。 将 {youriothubname} 替换为 IoT 中心名称,将 {yourdevicekey} 替换为在“创建设备标识”部分中生成的设备密钥值:

    private static String connString = "HostName={youriothubname}.azure-devices.cn;DeviceId=myDeviceID;SharedAccessKey={yourdevicekey}";
    private static IotHubClientProtocol protocol = IotHubClientProtocol.MQTT;
    private static String deviceId = "myDeviceId";
    

    本示例应用在实例化 DeviceClient 对象时使用 protocol 变量。

  9. 将以下代码添加到 main 方法,以便:

    • 创建用来与 IoT 中心通信的设备客户端。
    • 创建一个 Device 对象用于存储设备孪生属性。

      DeviceClient client = new DeviceClient(connString, protocol);
      
      // Create a Device object to store the device twin properties
      Device dataCollector = new Device() {
      // Print details when a property value changes
      @Override
      public void PropertyCall(String propertyKey, Object propertyValue, Object context) {
        System.out.println(propertyKey + " changed to " + propertyValue);
      }
      };
      
  10. 将以下代码添加到 main 方法,创建 connectivityType 报告属性并将其发送到 IoT 中心:

    try {
      // Open the DeviceClient and start the device twin services.
      client.open();
      client.startDeviceTwin(new DeviceTwinStatusCallBack(), null, dataCollector, null);
    
      // Create a reported property and send it to your IoT hub.
      dataCollector.setReportedProp(new Property("connectivityType", "cellular"));
      client.sendReportedProperties(dataCollector.getReportedProp());
    }
    catch (Exception e) {
      System.out.println("On exception, shutting down \n" + " Cause: " + e.getCause() + " \n" + e.getMessage());
      dataCollector.clean();
      client.close();
      System.out.println("Shutting down...");
    }
    
  11. 将以下代码添加到 main 方法的末尾。 按 Enter 键,等待一段时间让 IoT 中心报告设备孪生操作的状态:

    System.out.println("Press any key to exit...");
    
    Scanner scanner = new Scanner(System.in);
    scanner.nextLine();
    
    dataCollector.clean();
    client.close();
    
  12. 保存并关闭 simulated-device\src\main\java\com\mycompany\app\App.java 文件。

  13. 生成 simulated-device 应用并更正任何错误。 在命令提示符下,导航到 simulated-device 文件夹并运行以下命令:

    mvn clean package -DskipTests

运行应用

现在可以运行控制台应用了。

  1. add-tags-query 文件夹中的命令提示符下,运行以下命令以运行 add-tags-query 服务应用:

    mvn exec:java -Dexec.mainClass="com.mycompany.app.App"

    Java IoT 中心服务应用会更新标记值并运行设备查询

    可以看到,plantregion 标记已添加到设备孪生。 第一个查询返回设备,但第二个查询则不会。

  2. simulated-device 文件夹中的命令提示符下,运行以下命令将 connectivityType 报告属性添加到设备孪生:

    mvn exec:java -Dexec.mainClass="com.mycompany.app.App"

    设备客户端会添加 **connectivityType** 报告属性

  3. add-tags-query 文件夹中的命令提示符下,再次运行以下命令以运行 add-tags-query 服务应用:

    mvn exec:java -Dexec.mainClass="com.mycompany.app.App"

    Java IoT 中心服务应用会更新标记值并运行设备查询

    现在,设备已将 connectivityType 属性发送到 IoT 中心,第二个查询返回了设备。

后续步骤

本教程中,在 Azure 门户中配置了新的 IoT 中心,并在 IoT 中心的标识注册表中创建了设备标识。 已从后端应用以标记形式添加了设备元数据,并编写了一个设备应用用于报告设备孪生中的设备连接信息。 此外,还学习了如何使用类似于 SQL 的 IoT 中心查询语言查询设备孪生信息。

充分利用以下资源:

  • 通过 IoT 中心入门教程学习如何从设备发送遥测数据。
  • 通过使用直接方法教程学习如何以交互方式控制设备(例如从用户控制的应用打开风扇)。