设备管理入门 (Java)

本文介绍如何创建:

  • simulated-device:一个模拟设备应用,它使用直接方法重新启动设备并报告上次重新启动时间。 直接方法是从云中调用的。

  • trigger-reboot:一个 Java 应用,通过 IoT 中心调用模拟设备应用中的直接方法。 它显示响应和更新的报告属性。

注意

有关 SDK 的信息(可以使用这些 SDK 构建在设备和解决方案后端上运行的应用程序),请参阅 Azure IoT SDK

先决条件

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

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

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

  • Maven 3

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

使用直接方法创建设备应用

本部分中将创建一个模拟设备的 Java 控制台应用。 该应用侦听 IoT 中心发出的重启直接方法调用并快速响应该调用。 然后该应用会休眠一段时间,以模拟重启过程,之后该应用会使用报告属性通知 trigger-reboot 后端应用重启已完成。

  1. 在 dm-get-started 文件夹中,通过命令提示符使用以下命令创建名为 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-service-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.time.LocalDateTime;
    import java.util.Scanner;
    import java.util.Set;
    import java.util.HashSet;
    
  9. 将以下类级变量添加到 App 类。 将 {yourdeviceconnectionstring} 替换为在 IoT 中心注册设备时看到的设备连接字符串:

    private static final int METHOD_SUCCESS = 200;
    private static final int METHOD_NOT_DEFINED = 404;
    
    private static IotHubClientProtocol protocol = IotHubClientProtocol.MQTT;
    private static String connString = "{yourdeviceconnectionstring}";
    private static DeviceClient client;
    
  10. 若要为直接方法状态事件实现回调处理程序,请将以下嵌套类添加到 App 类:

    protected static class DirectMethodStatusCallback implements IotHubEventCallback
    {
      public void execute(IotHubStatusCode status, Object context)
      {
        System.out.println("IoT Hub responded to device method operation with status " + status.name());
      }
    }
    
  11. 若要为设备孪生状态事件实现回调处理程序,请将以下嵌套类添加到 App 类:

    protected static class DeviceTwinStatusCallback implements IotHubEventCallback
    {
        public void execute(IotHubStatusCode status, Object context)
        {
            System.out.println("IoT Hub responded to device twin operation with status " + status.name());
        }
    }
    
  12. 若要为属性事件实现回调处理程序,请将以下嵌套类添加到 App 类:

    protected static class PropertyCallback implements PropertyCallBack<String, String>
    {
      public void PropertyCall(String propertyKey, String propertyValue, Object context)
      {
        System.out.println("PropertyKey:     " + propertyKey);
        System.out.println("PropertyKvalue:  " + propertyKey);
      }
    }
    
  13. 若要实现一个模拟设备重启的线程,请将以下嵌套类添加到 App 类。 线程会休眠五秒,然后设置 lastReboot 报告属性:

    protected static class RebootDeviceThread implements Runnable {
      public void run() {
        try {
          System.out.println("Rebooting...");
          Thread.sleep(5000);
          Property property = new Property("lastReboot", LocalDateTime.now());
          Set<Property> properties = new HashSet<Property>();
          properties.add(property);
          client.sendReportedProperties(properties);
          System.out.println("Rebooted");
        }
        catch (Exception ex) {
          System.out.println("Exception in reboot thread: " + ex.getMessage());
        }
      }
    }
    
  14. 若要在设备上实现直接方法,请将以下嵌套类添加到 App 类。 模拟应用接收到重启直接方法的调用时,会向调用方返回确认,然后启动处理重启的线程:

    protected static class DirectMethodCallback implements com.microsoft.azure.sdk.iot.device.DeviceTwin.DeviceMethodCallback
    {
      @Override
      public DeviceMethodData call(String methodName, Object methodData, Object context)
      {
        DeviceMethodData deviceMethodData;
        switch (methodName)
        {
          case "reboot" :
          {
            int status = METHOD_SUCCESS;
            System.out.println("Received reboot request");
            deviceMethodData = new DeviceMethodData(status, "Started reboot");
            RebootDeviceThread rebootThread = new RebootDeviceThread();
            Thread t = new Thread(rebootThread);
            t.start();
            break;
          }
          default:
          {
            int status = METHOD_NOT_DEFINED;
            deviceMethodData = new DeviceMethodData(status, "Not defined direct method " + methodName);
          }
        }
        return deviceMethodData;
      }
    }
    
  15. 修改 main 方法的签名以引发以下异常:

    public static void main(String[] args) throws IOException, URISyntaxException
    
  16. 若要实例化 DeviceClient,请将 main 方法中的代码替换为以下代码:

    System.out.println("Starting device client sample...");
    client = new DeviceClient(connString, protocol);
    
  17. 若要开始侦听直接方法调用,请将以下代码添加到 main 方法:

    try
    {
      client.open();
      client.subscribeToDeviceMethod(new DirectMethodCallback(), null, new DirectMethodStatusCallback(), null);
      client.startDeviceTwin(new DeviceTwinStatusCallback(), null, new PropertyCallback(), null);
      System.out.println("Subscribed to direct methods and polling for reported properties. Waiting...");
    }
    catch (Exception e)
    {
      System.out.println("On exception, shutting down \n" + " Cause: " + e.getCause() + " \n" +  e.getMessage());
      client.close();
      System.out.println("Shutting down...");
    }
    
  18. 若要关闭设备模拟器,请将以下代码添加到 main 方法:

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

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

    mvn clean package -DskipTests
    

获取 IoT 中心连接字符串

在本文中,你将创建一项在设备上调用直接方法的后端服务。 若要通过 IoT 中心在设备上调用直接方法,服务需要“服务连接”权限。 默认情况下,每个 IoT 中心都使用名为“服务”的共享访问策略创建,该策略会授予此权限。

若要获取 service策略的 IoT 中心连接字符串,请执行以下步骤:

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

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

  3. 在策略列表中,选择“service”策略。

  4. 复制“主连接字符串”并保存该值。

Screenshot that shows how to retrieve the connection string from your IoT Hub in the Azure portal.

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

创建服务应用以触发重新启动

本部分将创建一个进行如下操作的 Java 控制台应用:

  1. 在模拟设备应用中调用重启直接方法。

  2. 显示响应。

  3. 轮询设备发送的报告属性,以确定重启的完成时间。

此控制台应用连接到 IoT 中心,以便调用该直接方法并读取报告属性。

  1. 创建名为 dm-get-started 的空文件夹。

  2. 在 dm-get-started 文件夹中,通过命令提示符使用以下命令创建名为 trigger-reboot 的 Maven 项目:

    mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=trigger-reboot -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
    
  3. 在命令提示符下,导航到 trigger-reboot 文件夹。

  4. 使用文本编辑器打开 trigger-reboot 文件夹中的 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. 使用文本编辑器打开 trigger-reboot\src\main\java\com\mycompany\app\App.java 源文件。

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

    import com.microsoft.azure.sdk.iot.service.devicetwin.DeviceMethod;
    import com.microsoft.azure.sdk.iot.service.devicetwin.MethodResult;
    import com.microsoft.azure.sdk.iot.service.exceptions.IotHubException;
    import com.microsoft.azure.sdk.iot.service.devicetwin.DeviceTwin;
    import com.microsoft.azure.sdk.iot.service.devicetwin.DeviceTwinDevice;
    
    import java.io.IOException;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ExecutorService;
    
  9. 将以下类级变量添加到 App 类。 将 {youriothubconnectionstring} 替换为以前在获取 IoT 中心连接字符串中复制的 IoT 中心连接字符串:

    public static final String iotHubConnectionString = "{youriothubconnectionstring}";
    public static final String deviceId = "myDeviceId";
    
    private static final String methodName = "reboot";
    private static final Long responseTimeout = TimeUnit.SECONDS.toSeconds(30);
    private static final Long connectTimeout = TimeUnit.SECONDS.toSeconds(5);
    
  10. 若要实现每隔 10 秒读取一次设备孪生提供的报告属性的线程,请将以下嵌套类添加到 App 类:

    private static class ShowReportedProperties implements Runnable {
      public void run() {
        try {
          DeviceTwin deviceTwins = DeviceTwin.createFromConnectionString(iotHubConnectionString);
          DeviceTwinDevice twinDevice = new DeviceTwinDevice(deviceId);
          while (true) {
            System.out.println("Get reported properties from device twin");
            deviceTwins.getTwin(twinDevice);
            System.out.println(twinDevice.reportedPropertiesToString());
            Thread.sleep(10000);
          }
        } catch (Exception ex) {
          System.out.println("Exception reading reported properties: " + ex.getMessage());
        }
      }
    }
    
  11. 将 main 方法签名修改为抛出以下异常:

    public static void main(String[] args) throws IOException
    
  12. 若要在模拟设备上调用重启直接方法,请将 main 方法中的代码替换为以下代码:

    System.out.println("Starting sample...");
    DeviceMethod methodClient = DeviceMethod.createFromConnectionString(iotHubConnectionString);
    
    try
    {
      System.out.println("Invoke reboot direct method");
      MethodResult result = methodClient.invoke(deviceId, methodName, responseTimeout, connectTimeout, null);
    
      if(result == null)
      {
        throw new IOException("Invoke direct method reboot returns null");
      }
      System.out.println("Invoked reboot on device");
      System.out.println("Status for device:   " + result.getStatus());
      System.out.println("Message from device: " + result.getPayload());
    }
    catch (IotHubException e)
    {
        System.out.println(e.getMessage());
    }
    
  13. 若要启动轮询模拟设备提供的报告属性的线程,请将以下代码添加到 main 方法:

    ShowReportedProperties showReportedProperties = new ShowReportedProperties();
    ExecutorService executor = Executors.newFixedThreadPool(1);
    executor.execute(showReportedProperties);
    
  14. 若要能够停止应用,请将以下代码添加到 main 方法:

    System.out.println("Press ENTER to exit.");
    System.in.read();
    executor.shutdownNow();
    System.out.println("Shutting down sample...");
    
  15. 保存并关闭 trigger-reboot\src\main\java\com\mycompany\app\App.java 文件。

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

    mvn clean package -DskipTests
    

运行应用

现在可以运行应用了。

  1. 在命令提示符下,在 simulated-device 文件夹中运行以下命令,开始侦听 IoT 中心发出的重启方法调用:

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

    Java IoT Hub simulated device app to listen for reboot direct method calls

  2. 在命令提示符下,在 trigger-reboot 文件夹中运行以下命令,在模拟设备上从 IoT 中心调用重启方法:

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

    Java IoT Hub service app to call the reboot direct method

  3. 模拟设备对重启直接方法调用做出响应:

    Java IoT Hub simulated device app responds to the direct method call

自定义和扩展设备管理操作

IoT 解决方案可扩展已定义的设备管理模式集,或通过使用设备孪生和云到设备方法基元启用自定义模式。 设备管理操作的其他示例包括恢复出厂设置、固件更新、软件更新、电源管理、网络和连接管理以及数据加密。

设备维护时段

通常情况下,将设备配置为在某一时间执行操作,以最大程度减少中断和停机时间。 设备维护时段是一种常用模式,用于定义设备应更新其配置的时间。 后端解决方案使用设备克隆所需属性在设备上定义并激活策略,以启用维护时段。 当设备收到维护时段策略时,它可以使用设备克隆报告属性报告策略状态。 然后,后端应用可以使用设备克隆查询来证明设备和每个策略的符合性。

后续步骤

本文使用直接方法触发设备上的远程重新启动。 使用报告属性报告设备上次重新启动时间,并查询设备孪生从云中发现设备上次重新启动时间。

若要继续完成 IoT 中心和设备管理模式(如远程无线固件更新)的入门内容,请参阅如何更新固件

若要了解如何扩展 IoT 解决方案以及在多个设备上计划方法调用,请参阅计划和广播作业