设备孪生入门 (Java)

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

注意

本文所述的功能只能用于 IoT 中心的标准层。 有关 IoT 中心基本层和标准/免费层的详细信息,请参阅选择适合你的解决方案的 IoT 中心层

使用设备克隆可以:

  • 存储来自解决方案后端的设备元数据。

  • 通过设备应用报告当前状态信息,例如可用功能和条件(例如,使用的连接方法)。

  • 同步设备应用和后端应用之间长时间运行的工作流(例如固件和配置更新)的状态。

  • 查询设备的元数据、配置或状态。

设备孪生旨在执行同步以及查询设备的配置和条件。 有关设备孪生的详细信息(包括何时使用设备孪生),请参阅了解设备孪生

IoT 中心存储包含以下元素的设备孪生:

  • 标记。 只能由解决方案后端访问的设备元数据。

  • 所需属性。 可以由解决方案后端修改以及由设备应用观察的 JSON 对象。

  • 报告属性。 可以由设备应用修改以及由解决方案后端读取的 JSON 对象。

标记和属性不能包含数组,但可以包含嵌套对象。

下图显示了设备孪生组织:

Screenshot of a device twin concept diagram.

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

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

  • add-tags-query:一个后端应用,用于添加标记并查询设备孪生。
  • simulated-device:一个模拟设备应用,用于连接到 IoT 中心并报告其连接状况。

备注

有关可用于生成设备和后端应用的 SDK 工具的详细信息,请参阅 Azure IoT SDK

先决条件

  • IoT 中心。 使用 CLIAzure 门户创建一个。

  • 已注册的设备。 在 Azure 门户中注册一个。

  • Java SE 开发工具包 8。 请确保在“长期支持”下选择“Java 8”以获取 JDK 8 的下载。

  • Maven 3

  • 确保已在防火墙中打开端口 8883。 本文中的设备示例使用 MQTT 协议,该协议通过端口 8883 进行通信。 在某些公司和教育网络环境中,此端口可能被阻止。 有关解决此问题的更多信息和方法,请参阅连接到 IoT 中心(MQTT)

获取 IoT 中心连接字符串

在本文中,你将创建一项后端服务,该服务将所需的属性添加到设备孪生,然后查询标识注册表,从而查找具有已相应更新的报告属性的所有设备。 服务需要“服务连接”权限才能修改设备孪生的所需属性,并且需要“注册表读取”权限才能查询标识注册表。 没有仅包含这两个权限的默认共享访问策略,因此需要创建一个。

若要创建授予“服务连接”和“注册表读取”权限的共享访问策略并获取此策略的连接字符串,请执行以下步骤:

  1. Azure 门户中,选择“资源组”。 选择中心所在的资源组,然后从资源列表中选择中心。

  2. 在中心的左侧窗格上,选择“共享访问策略”。

  3. 在策略列表上方的顶部菜单中,选择“添加共享策略访问策略”。

  4. 在右侧的“添加共享访问策略”窗格中,为策略输入一个描述性名称,例如 serviceAndRegistryRead。 在“权限”下,选择“注册表读取”和“服务连接”,然后选择“添加”。

    Screen capture that shows how to add a new shared access policy.

  5. 从策略列表中选择新策略。

  6. 选择“主连接字符串”的复制图标并保存值。

    Screen capture that shows how to retrieve the connection string.

有关 IoT 中心共享访问策略和权限的详细信息,请参阅访问控制和权限

创建更新报告的属性的设备应用

在本部分中,将创建一个 Java 控制台应用作为 myDeviceId 连接到中心,然后更新其设备孪生的报告属性,以确认它使用手机网络进行连接。

  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.17.5</version>
    </dependency>
    

    注意

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

  4. 将以下依赖项添加到 dependencies 节点。 此依赖项为 Apache SLF4J 日志记录外观配置 NOP,设备客户端 SDK 使用它实现日志记录。 此配置是可选的,但是如果省略此配置,则在运行该应用时,可能会在控制台中看到警告。 有关设备客户端 SDK 中的日志记录的详细信息,请参阅适用于 Java 的 Azure IoT 设备 SDK 示例自述文件中的日志记录

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.28</version>
    </dependency>
    
  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. 使用文本编辑器打开 simulated-device\src\main\java\com\mycompany\app\App.java 文件。

  8. 在该文件中添加以下 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;
    
  9. 将以下类级变量添加到 App 类。 将 {yourdeviceconnectionstring} 替换为在 IoT 中心注册设备时看到的设备连接字符串:

    private static String connString = "{yourdeviceconnectionstring}";
    private static IotHubClientProtocol protocol = IotHubClientProtocol.MQTT;
    private static String deviceId = "myDeviceId";
    

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

  10. 将以下方法添加到 App 类以输出有关孪生更新的信息:

    protected static class DeviceTwinStatusCallBack implements IotHubEventCallback {
        @Override
        public void execute(IotHubStatusCode status, Object context) {
          System.out.println("IoT Hub responded to device twin operation with status " + status.name());
        }
      }
    
  11. 将 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);
      }
    };
    
  12. 将以下代码添加到 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.closeNow();
      System.out.println("Shutting down...");
    }
    
  13. 将以下代码添加到 main 方法的末尾。 按 Enter 键,等待一段时间让 IoT 中心报告设备孪生操作的状态。

    System.out.println("Press any key to exit...");
    
    Scanner scanner = new Scanner(System.in);
    scanner.nextLine();
    
    dataCollector.clean();
    client.close();
    
  14. 修改 main 方法的签名,包含如下所示的异常:

    public static void main(String[] args) throws URISyntaxException, IOException
    
  15. 保存并关闭 simulated-device\src\main\java\com\mycompany\app\App.java 文件。

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

    mvn clean package -DskipTests
    

创建更新所需属性和查询孪生的服务应用

本部分将创建一个 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.17.1</version>
      <type>jar</type>
    </dependency>
    

    注意

    可以使用 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 方法中的代码替换为以下代码,以创建 DeviceTwin 和 DeviceTwinDevice 对象。 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
    

运行应用

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

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

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

    Screenshot that shows the output from the command to run the add tags query service app.

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

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

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

    The device client adds the connectivity Type reported property

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

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

    Java IoT Hub service app to update tag values and run device queries

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

本文内容:

  • 从后端应用添加了设备元数据作为标记
  • 在设备孪生中报告了设备连接信息
  • 使用类似 SQL 的 IoT 中心查询语言查询了设备孪生信息

后续步骤

若要了解操作方法: