将设备连接到远程监控预配置解决方案 (Windows)

本教程实施一个可将以下遥测数据发送到远程监控预配置解决方案冷却器设备:

  • 温度
  • 压力
  • 湿度

为简单起见,代码会生成冷却器的示例遥测值。 可以通过将真实的传感器连接到设备并发送真实的遥测数据,在本示例的基础上融会贯通。

示例设备还会:

  • 将元数据发送到解决方案,以描述设备的功能。
  • 针对通过解决方案中的“设备”页触发的操作做出响应。
  • 针对通过解决方案中的“设备”页发送的配置更改做出响应。

要完成此教程,需要一个有效的 Azure 帐户。 如果没有帐户,可以创建一个试用帐户,只需几分钟即可完成。 有关详细信息,请参阅 [Azure 试用][lnk-1rmb-trial]。

开始之前

在为设备编写任何代码之前,必须先预配远程监控预配置解决方案,并在该解决方案中预配新的自定义设备。

预配远程监控预配置解决方案

本教程中创建的冷却器设备会将数据发送到远程监控预配置解决方案的实例中。 如果尚未在 Azure 帐户中预配远程监控预配置解决方案,请参阅部署远程监控预配置解决方案

预配好远程监控解决方案后,单击“启动” ,在浏览器中打开解决方案仪表板。

解决方案仪表板

在远程监控方案中预配设备

Note

如果已在解决方案中预配了设备,则可以跳过此步骤。 创建客户端应用程序时需要设备凭据。

连接到预配置解决方案的设备必须能够使用有效凭据对 IoT 中心识别自身。 可以在解决方案的“设备”页中检索设备凭据。 本教程后文中的客户端应用程序要采用该设备凭据。

若要在远程监控解决方案中添加设备,请在解决方案中的“设备”页上完成以下步骤:

  1. 选择“预配”,并选择“物理”作为设备类型

    预配物理设备

  2. 输入 Physical-chiller 作为设备 ID。 选择“对称密钥”和“自动生成密钥”选项:

    选择设备选项

若要找到设备在连接到预配置解决方案时必须使用的凭据,请在浏览器中导航到 Azure 门户。 登录到订阅。

  1. 找到包含远程监控解决方案所用 Azure 服务的资源组。 该资源组与预配的远程监控解决方案同名。

  2. 在此资源组中导航到 IoT 中心。 然后选择“IoT 设备”:

    设备资源管理器

  3. 选择在远程监控解决方案中的“设备”页上创建的设备 ID

  4. 记下“设备 ID”和“设备密钥”值。 添加用于将设备连接到解决方案的代码时,将要使用这些值。

现已在远程监控预配置解决方案中预配了一个物理设备。 在以下部分中,我们将会实现使用设备凭据连接到解决方案的客户端应用程序。

客户端应用程序实现内置的冷却器设备模型。 预配置解决方案设备模型指定有关设备的以下信息:

  • 设备报告给解决方案的属性。 例如,冷却器设备报告有关其固件和位置的信息。
  • 由设备发送到解决方案的遥测数据类型。 例如,冷却器设备发送温度、湿度和压力值。
  • 可以在解决方案中计划的、要在设备上运行的方法。 例如,冷却器设备必须实现 RebootFirmwareUpdateEmergencyValveReleaseIncreasePressuree 方法。

本教程介绍如何将物理设备连接到远程监控预配置解决方案。

在 Windows 上创建 C 客户端解决方案

与受约束设备上运行的大多数嵌入式应用程序一样,设备应用程序的客户端代码是用 C 语言编写的。在本教程中,将在运行 Windows 的计算机上生成应用程序。

创建初学者项目

在 Visual Studio 2017 中创建初学者项目并添加 IoT 中心设备客户端 NuGet 包:

  1. 在 Visual Studio 中,使用 Visual C++ Windows 控制台应用程序模板创建一个 C 控制台应用程序。 将该项目命名为 RMDevice

    创建 Visual C++ Windows 控制台应用程序

  2. 在“解决方案资源管理器”中,删除文件 stdafx.htargetver.hstdafx.cpp

  3. 在“解决方案资源管理器”中,将文件 RMDevice.cpp 重命名为 RMDevice.c

    显示已重命名的 RMDevice.c 文件的解决方案资源管理器

  4. 在“解决方案资源管理器”中,右键单击“RMDevice”项目,并单击“管理 NuGet 包”。 选择“浏览”,搜索并安装以下 NuGet 包:

    • Microsoft.Azure.IoTHub.Serializer
    • Microsoft.Azure.IoTHub.IoTHubClient
    • Microsoft.Azure.IoTHub.MqttTransport

      NuGet 包管理器显示已安装的 Microsoft.Azure.IoTHub 包

  5. 在“解决方案资源管理器”中,右键单击“RMDevice”项目,并选择“属性”打开该项目的“属性页”对话框。 有关详细信息,请参阅设置 Visual C++ 项目属性

  6. 选择“C/C++”文件夹,再选择“预编译标头”属性页。

  7. 将“预编译标头”设置为“不使用预编译标头”。 然后选择“应用”。

    项目属性显示项目不使用预编译标头

  8. 选择“Linker”文件夹,再选择单击“输入”属性页。

  9. crypt32.lib 添加到“其他依赖项”属性。 若要保存项目属性值,请选择“确定”,然后再次选择“确定”。

    项目属性显示包括 crypt32.lib 的链接器

添加 Parson JSON 库

将 Parson JSON 库添加到 RMDevice 项目,并添加所需的 #include 语句:

  1. 在计算机上的适当文件夹中,使用以下命令克隆 Parson GitHub 存储库:

    git clone https://github.com/kgabis/parson.git
    
  2. parson.hparson.c 文件从 Parson 存储库的本地副本复制到 RMDevice 项目文件夹。

  3. 在 Visual Studio 中,右键单击“RMDevice”项目,选择“添加”,再选择“现有项”。

  4. 在“添加现有项”对话框中,选择“RMDevice”项目文件夹中的 parson.hparson.c 文件。 若要将这两个文件添加到项目,请选择“添加”。

    解决方案资源管理器显示 parson.h 和 parson.c 文件

  5. 在 Visual Studio 中打开 RMDevice.c 文件。 将现有 #include 语句替换为以下代码:

    #include "iothubtransportmqtt.h"
    #include "schemalib.h"
    #include "iothub_client.h"
    #include "serializer_devicetwin.h"
    #include "schemaserializer.h"
    #include "azure_c_shared_utility/threadapi.h"
    #include "azure_c_shared_utility/platform.h"
    #include <string.h>
    

    Note

    现在可以通过生成解决方案来验证该项目是否已设置正确的依赖项。

指定 IoT 中心设备的行为

IoT 中心序列化程序客户端库使用模型来指定设备与 IoT 中心交换的消息的格式。

  1. #include 语句之后添加以下变量声明。 将占位符值 [Device Id][Device Key] 替换为在远程监控解决方案仪表板中记下的设备值。 使用解决方案仪表板中的 IoT 中心主机名替换 [IoTHub Name]。 例如,如果 IoT 中心主机名是 contoso.azure-devices.cn,则将 [IoTHub Name] 替换为 contoso

    static const char* deviceId = "[Device Id]";
    static const char* connectionString = "[Device connection string]";
    
  2. 添加以下代码以定义使设备可以与 IoT 中心通信的模型。 此模型指定设备可执行以下操作:

    • 可以将温度、压力和湿度作为遥测数据发送。
    • 可以向 IoT 中心中的设备孪生发送已报告的属性。 这些报告属性包括有关遥测架构和受支持方法的信息。
    • 可以接收和处理在 IoT 中心的设备孪生中设置的所需属性。
    • 可响应从 UI 调用的 RebootFirmwareUpdateEmergencyValveReleaseIncreasePressure 直接方法。 设备使用报告的属性发送有关其支持的直接方法的信息。

      // Define the Model
      BEGIN_NAMESPACE(Contoso);
      
      DECLARE_STRUCT(MessageSchema,
      ascii_char_ptr, Name,
      ascii_char_ptr, Format,
      ascii_char_ptr_no_quotes, Fields
      )
      
      DECLARE_STRUCT(TelemetrySchema,
      ascii_char_ptr, Interval,
      ascii_char_ptr, MessageTemplate,
      MessageSchema, MessageSchema
      )
      
      DECLARE_STRUCT(TelemetryProperties,
      TelemetrySchema, TemperatureSchema,
      TelemetrySchema, HumiditySchema,
      TelemetrySchema, PressureSchema
      )
      
      DECLARE_DEVICETWIN_MODEL(Chiller,
      /* Telemetry (temperature, external temperature and humidity) */
      WITH_DATA(double, temperature),
      WITH_DATA(ascii_char_ptr, temperature_unit),
      WITH_DATA(double, pressure),
      WITH_DATA(ascii_char_ptr, pressure_unit),
      WITH_DATA(double, humidity),
      WITH_DATA(ascii_char_ptr, humidity_unit),
      
      /* Manage firmware update process */
      WITH_DATA(ascii_char_ptr, new_firmware_URI),
      WITH_DATA(ascii_char_ptr, new_firmware_version),
      
      /* Device twin properties */
      WITH_REPORTED_PROPERTY(ascii_char_ptr, Protocol),
      WITH_REPORTED_PROPERTY(ascii_char_ptr, SupportedMethods),
      WITH_REPORTED_PROPERTY(TelemetryProperties, Telemetry),
      WITH_REPORTED_PROPERTY(ascii_char_ptr, Type),
      WITH_REPORTED_PROPERTY(ascii_char_ptr, Firmware),
      WITH_REPORTED_PROPERTY(ascii_char_ptr, FirmwareUpdateStatus),
      WITH_REPORTED_PROPERTY(ascii_char_ptr, Location),
      WITH_REPORTED_PROPERTY(double, Latitiude),
      WITH_REPORTED_PROPERTY(double, Longitude),
      
      WITH_DESIRED_PROPERTY(ascii_char_ptr, Interval, onDesiredInterval),
      
      /* Direct methods implemented by the device */
      WITH_METHOD(Reboot),
      WITH_METHOD(FirmwareUpdate, ascii_char_ptr, Firmware, ascii_char_ptr, FirmwareUri),
      WITH_METHOD(EmergencyValveRelease),
      WITH_METHOD(IncreasePressure)
      );
      
      END_NAMESPACE(Contoso);
      

实现设备的行为

现在添加实现模型中定义的行为的代码。

  1. 添加以下回调处理程序,设备向预配置解决方案发送新的报告属性值后,会运行该处理程序:

    /* Callback after sending reported properties */
    void deviceTwinCallback(int status_code, void* userContextCallback)
    {
      (void)(userContextCallback);
      printf("IoTHub: reported properties delivered with status_code = %u\n", status_code);
    }
    
  2. 添加以下函数,以模拟固件更新过程:

    static int do_firmware_update(void *param)
    {
      Chiller *chiller = (Chiller *)param;
      printf("do_firmware_update('URI: %s, Version: %s')\r\n", chiller->new_firmware_URI, chiller->new_firmware_version);
    
      printf("Simulating download phase...\r\n");
      chiller->FirmwareUpdateStatus = "downloading";
      /* Send reported properties to IoT Hub */
      if (IoTHubDeviceTwin_SendReportedStateChiller(chiller, deviceTwinCallback, NULL) != IOTHUB_CLIENT_OK)
      {
        printf("Failed sending serialized reported state\r\n");
      }
      ThreadAPI_Sleep(5000);
    
      printf("Simulating applying phase...\r\n");
      chiller->FirmwareUpdateStatus = "applying";
      /* Send reported properties to IoT Hub */
      if (IoTHubDeviceTwin_SendReportedStateChiller(chiller, deviceTwinCallback, NULL) != IOTHUB_CLIENT_OK)
      {
        printf("Failed sending serialized reported state\r\n");
      }
      ThreadAPI_Sleep(5000);
    
      printf("Simulating reboot phase...\r\n");
      chiller->FirmwareUpdateStatus = "rebooting";
      /* Send reported properties to IoT Hub */
      if (IoTHubDeviceTwin_SendReportedStateChiller(chiller, deviceTwinCallback, NULL) != IOTHUB_CLIENT_OK)
      {
        printf("Failed sending serialized reported state\r\n");
      }
      ThreadAPI_Sleep(5000);
    
      chiller->Firmware = _strdup(chiller->new_firmware_version);
      chiller->FirmwareUpdateStatus = "waiting";
      /* Send reported properties to IoT Hub */
      if (IoTHubDeviceTwin_SendReportedStateChiller(chiller, deviceTwinCallback, NULL) != IOTHUB_CLIENT_OK)
      {
        printf("Failed sending serialized reported state\r\n");
      }
    
      return 0;
    }
    
  3. 添加以下函数,以处理在解决方案仪表板中设置的所需属性。 模型中定义了以下所需属性:

    void onDesiredInterval(void* argument)
    {
      /* By convention 'argument' is of the type of the MODEL */
      Chiller* chiller = argument;
      printf("Received a new desired Interval value: %s \r\n", chiller->Interval);
    }
    
  4. 添加以下函数,用于处理通过 IoT 中心调用的直接方法。 模型中定义了以下直接方法:

    /* Handlers for direct methods */
    METHODRETURN_HANDLE Reboot(Chiller* chiller)
    {
      (void)(chiller);
    
      METHODRETURN_HANDLE result = MethodReturn_Create(201, "\"Rebooting\"");
      printf("Received reboot request\r\n");
      return result;
    }
    
    METHODRETURN_HANDLE FirmwareUpdate(Chiller* chiller, ascii_char_ptr Firmware, ascii_char_ptr FirmwareUri)
    {
      printf("Recieved firmware update request request\r\n");
      METHODRETURN_HANDLE result = NULL;
      if (chiller->FirmwareUpdateStatus != "waiting")
      {
        LogError("Attempting to initiate a firmware update out of order");
        result = MethodReturn_Create(400, "\"Attempting to initiate a firmware update out of order\"");
      }
      else
      {
        chiller->new_firmware_version = _strdup(Firmware);
        chiller->new_firmware_URI = _strdup(FirmwareUri);
        THREAD_HANDLE thread_apply;
        THREADAPI_RESULT t_result = ThreadAPI_Create(&thread_apply, do_firmware_update, chiller);
        if (t_result == THREADAPI_OK)
        {
          result = MethodReturn_Create(201, "\"Starting firmware update thread\"");
        }
        else
        {
          LogError("Failed to start firmware update thread");
          result = MethodReturn_Create(500, "\"Failed to start firmware update thread\"");
        }
      }
    
      return result;
    }
    
    METHODRETURN_HANDLE EmergencyValveRelease(Chiller* chiller)
    {
      (void)(chiller);
    
      METHODRETURN_HANDLE result = MethodReturn_Create(201, "\"Releasing Emergency Valve\"");
      printf("Recieved emergency valve release request\r\n");
      return result;
    }
    
    METHODRETURN_HANDLE IncreasePressure(Chiller* chiller)
    {
      (void)(chiller);
    
      METHODRETURN_HANDLE result = MethodReturn_Create(201, "\"Increasing Pressure\"");
      printf("Received increase pressure request\r\n");
      return result;
    }
    
  5. 添加以下函数以便将属性添加到设备到云消息:

    /* Add message property */
    static void addProperty(MAP_HANDLE propMap, char* propName, char* propValue)
    {
      if (Map_AddOrUpdate(propMap, propName, propValue) != MAP_OK)
      {
        (void)printf("ERROR: Map_AddOrUpdate Failed on %s!\r\n", propName);
      }
    }
    
  6. 添加以下函数以便使用属性向预配置解决方案发送消息:

    static void sendMessage(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const unsigned char* buffer, size_t size, char* schema)
    {
      IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(buffer, size);
      if (messageHandle == NULL)
      {
        printf("unable to create a new IoTHubMessage\r\n");
      }
      else
      {
        // Add properties
        MAP_HANDLE propMap = IoTHubMessage_Properties(messageHandle);
        addProperty(propMap, "$$MessageSchema", schema);
        addProperty(propMap, "$$ContentType", "JSON");
        time_t now = time(0);
        struct tm* timeinfo;
        #pragma warning(disable: 4996)
        timeinfo = gmtime(&now);
        char timebuff[50];
        strftime(timebuff, 50, "%Y-%m-%dT%H:%M:%SZ", timeinfo);
        addProperty(propMap, "$$CreationTimeUtc", timebuff);
    
        if (IoTHubClient_SendEventAsync(iotHubClientHandle, messageHandle, NULL, NULL) != IOTHUB_CLIENT_OK)
        {
          printf("failed to hand over the message to IoTHubClient");
        }
        else
        {
          printf("IoTHubClient accepted the message for delivery\r\n");
        }
    
        IoTHubMessage_Destroy(messageHandle);
      }
      free((void*)buffer);
    }
    
  7. 添加以下函数,用于将设备连接到云中的预配置解决方案并交换数据。 此函数执行以下步骤:

    • 初始化平台。
    • 向序列化库注册 Contoso 命名空间。
    • 使用设备连接字符串初始化客户端。
    • 创建冷却器模型的实例。
    • 创建并发送报告属性值。
    • 创建一个循环,以便在固件更新状态为“正在等待”时每隔五秒发送一次遥测数据。
    • 取消初始化所有资源。

      void remote_monitoring_run(void)
      {
      if (platform_init() != 0)
      {
        printf("Failed to initialize the platform.\r\n");
      }
      else
      {
        if (SERIALIZER_REGISTER_NAMESPACE(Contoso) == NULL)
        {
          printf("Unable to SERIALIZER_REGISTER_NAMESPACE\r\n");
        }
        else
        {
          IOTHUB_CLIENT_HANDLE iotHubClientHandle = IoTHubClient_CreateFromConnectionString(connectionString, MQTT_Protocol);
          if (iotHubClientHandle == NULL)
          {
            printf("Failure in IoTHubClient_CreateFromConnectionString\r\n");
          }
          else
          {
            Chiller* chiller = IoTHubDeviceTwin_CreateChiller(iotHubClientHandle);
            if (chiller == NULL)
            {
              printf("Failure in IoTHubDeviceTwin_CreateChiller\r\n");
            }
            else
            {
              /* Set values for reported properties */
              chiller->Protocol = "MQTT";
              chiller->SupportedMethods = "Reboot,FirmwareUpdate,EmergencyValveRelease,IncreasePressure";
              chiller->Telemetry.TemperatureSchema.Interval = "00:00:05";
              chiller->Telemetry.TemperatureSchema.MessageTemplate = "{\"temperature\":${temperature},\"temperature_unit\":\"${temperature_unit}\"}";
              chiller->Telemetry.TemperatureSchema.MessageSchema.Name = "chiller-temperature;v1";
              chiller->Telemetry.TemperatureSchema.MessageSchema.Format = "JSON";
              chiller->Telemetry.TemperatureSchema.MessageSchema.Fields = "{\"temperature\":\"Double\",\"temperature_unit\":\"Text\"}";
              chiller->Telemetry.HumiditySchema.Interval = "00:00:05";
              chiller->Telemetry.HumiditySchema.MessageTemplate = "{\"humidity\":${humidity},\"humidity_unit\":\"${humidity_unit}\"}";
              chiller->Telemetry.HumiditySchema.MessageSchema.Name = "chiller-humidity;v1";
              chiller->Telemetry.HumiditySchema.MessageSchema.Format = "JSON";
              chiller->Telemetry.HumiditySchema.MessageSchema.Fields = "{\"humidity\":\"Double\",\"humidity_unit\":\"Text\"}";
              chiller->Telemetry.PressureSchema.Interval = "00:00:05";
              chiller->Telemetry.PressureSchema.MessageTemplate = "{\"pressure\":${pressure},\"pressure_unit\":\"${pressure_unit}\"}";
              chiller->Telemetry.PressureSchema.MessageSchema.Name = "chiller-pressure;v1";
              chiller->Telemetry.PressureSchema.MessageSchema.Format = "JSON";
              chiller->Telemetry.PressureSchema.MessageSchema.Fields = "{\"pressure\":\"Double\",\"pressure_unit\":\"Text\"}";
              chiller->Type = "Chiller";
              chiller->Firmware = "1.0.0";
              chiller->FirmwareUpdateStatus = "waiting";
              chiller->Location = "Building 44";
              chiller->Latitiude = 47.638928;
              chiller->Longitude = -122.13476;
      
              /* Send reported properties to IoT Hub */
              if (IoTHubDeviceTwin_SendReportedStateChiller(chiller, deviceTwinCallback, NULL) != IOTHUB_CLIENT_OK)
              {
                printf("Failed sending serialized reported state\r\n");
              }
              else
              {
                /* Send telemetry */
                chiller->temperature_unit = "F";
                chiller->pressure_unit = "psig";
                chiller->humidity_unit = "%";
      
                srand((unsigned int)time(NULL));
                while (1)
                {
                  chiller->temperature = 50 + ((rand() % 10) - 5);
                  chiller->pressure = 55 + ((rand() % 10) - 5);
                  chiller->humidity = 30 + ((rand() % 10) - 5);
                  unsigned char*buffer;
                  size_t bufferSize;
      
                  if (chiller->FirmwareUpdateStatus == "waiting")
                  {
                    (void)printf("Sending sensor value Temperature = %f %s,\r\n", chiller->temperature, chiller->temperature_unit);
      
                    if (SERIALIZE(&buffer, &bufferSize, chiller->temperature, chiller->temperature_unit) != CODEFIRST_OK)
                    {
                      (void)printf("Failed sending sensor value\r\n");
                    }
                    else
                    {
                      sendMessage(iotHubClientHandle, buffer, bufferSize, chiller->Telemetry.TemperatureSchema.MessageSchema.Name);
                    }
      
                    (void)printf("Sending sensor value Humidity = %f %s,\r\n", chiller->humidity, chiller->humidity_unit);
      
                  if (SERIALIZE(&buffer, &bufferSize, chiller->humidity, chiller->humidity_unit) != CODEFIRST_OK)
                  {
                    (void)printf("Failed sending sensor value\r\n");
                  }
                  else
                  {
                    sendMessage(iotHubClientHandle, buffer, bufferSize, chiller->Telemetry.HumiditySchema.MessageSchema.Name);
                  }
      
                    (void)printf("Sending sensor value Pressure = %f %s,\r\n", chiller->pressure, chiller->pressure_unit);
      
                  if (SERIALIZE(&buffer, &bufferSize, chiller->pressure, chiller->pressure_unit) != CODEFIRST_OK)
                  {
                    (void)printf("Failed sending sensor value\r\n");
                  }
                  else
                  {
                    sendMessage(iotHubClientHandle, buffer, bufferSize, chiller->Telemetry.PressureSchema.MessageSchema.Name);
                  }
      
                  ThreadAPI_Sleep(5000);
                }
      
                IoTHubDeviceTwin_DestroyChiller(chiller);
              }
            }
            IoTHubClient_Destroy(iotHubClientHandle);
          }
          serializer_deinit();
        }
      }
      platform_deinit();
      }
      

      下面提供了发送到预配置解决方案的示例 遥测 消息供参考:

      Device: [myCDevice],
      Data:[{"humidity":50.000000000000000, "humidity_unit":"%"}]
      Properties:
      '$$MessageSchema': 'chiller-humidity;v1'
      '$$ContentType': 'JSON'
      '$$CreationTimeUtc': '2017-09-12T09:17:13Z'
      

生成并运行示例

添加调用 remote_monitoring_run 函数的代码,然后生成并运行设备应用程序。

  1. 若要调用 remote_monitoring_run 函数,请将 main 函数替换为以下代码:

    int main()
    {
      remote_monitoring_run();
      return 0;
    }
    
  2. 选择“生成”,并单击“生成解决方案”以生成设备应用程序。

  3. 在“解决方案资源管理器”中,右键单击“RMDevice”项目,选择“调试”,再选择“启动新实例”以运行示例。 控制台中的消息显示为:

    • 应用程序将示例遥测数据发送到预配置解决方案。
    • 接收在解决方案仪表板中设置的所需属性值。
    • 响应从解决方案仪表板调用的方法。

查看设备遥测数据

可以在解决方案中的“设备”页上查看从设备发送的遥测数据。

  1. 在“设备”页上的设备列表中选择已预配的设备。 一个面板将显示有关设备的信息,其中包括设备遥测绘图:

    查看设备详细信息

  2. 选择“压力”可更改遥测显示:

    查看压力遥测

  3. 若要查看有关设备的诊断信息,请向下滚动到“诊断”:

    查看设备诊断

对设备执行操作

若要对设备调用方法,请使用远程监控解决方案中的“设备”页。 例如,在远程监控解决方案中,冷却器设备实现了FirmwareUpdate方法。

  1. 选择“设备”可导航到解决方案中的“设备”页。

  2. 在“设备”页上的设备列表中选择已预配的设备:

    选择物理设备

  3. 若要显示可对设备调用的方法列表,请选择“计划”。 若要计划要在多个设备上运行的方法,可以在列表中选择多个设备。 “计划”面板将显示普遍适用于所选择的所有设备的方法类型。

  4. 选择 FirmwareUpdate,将作业名称设置为 UpdatePhysicalChiller。 将“固件版本”设置为 2.0.0,将“固件 URI”设置为 http://contoso.com/updates/firmware.bin,然后选择“应用”:

    计划固件更新

  5. 模拟设备处理该方法时,一系列消息将显示在运行设备代码的控制台中。

  6. 更新完成时,“设备”页上将显示新的固件版本:

    更新已完成

Note

若要跟踪解决方案中作业的状态,请选择“查看”。

后续步骤

自定义远程监控预配置的解决方案一文中介绍了自定义预配置的解决方案的一些方法。