创建并测试新的模拟设备

借助远程监视解决方案加速器,可以定义自己的模拟设备。 本文介绍如何定义新的模拟灯泡设备,然后在本地对其进行测试。 解决方案加速器包括诸如冷却器和卡车的模拟设备。 但是,可以定义自己的模拟设备以在部署实际设备之前对 IoT 解决方案进行测试。

注意

本文介绍如何使用设备模拟服务中托管的模拟设备。 如果想要创建真实设备,请参阅将设备连接到远程监视解决方案加速器

本操作说明指南介绍如何自定义设备模拟微服务。 此微服务是远程监视解决方案加速器的一部分。 为了演示设备模拟功能,本操作说明指南在 Contoso IoT 应用程序中使用了两个方案:

在第一个方案中,我们将新的遥测类型添加到 Contoso 的现有“冷却器”设备类型

在第二个方案中,Contoso 想要测试新的智能灯泡设备。 为了运行测试,我们创建了具有以下特征的新模拟设备:

属性

名称
Color 白、红、蓝
亮度 0 到 100
估计剩余生命 从 10,000 小时开始倒计数

遥测

下表显示了灯泡以数据流形式报告给云的数据:

名称
状态 "on"、"off"
温度 华氏度
联机 true、false

注意

对于所有模拟类型来说,联机遥测值是必需的。

Methods

下表显示了新设备支持的操作:

名称
打开
关闭

初始状态

下表显示了设备的初始状态:

名称
初始颜色 白色
初始亮度 75
初始剩余生命 10,000
初始遥测状态 "on"
初始遥测温度 200

完成本操作方法指南中的步骤需要有效的 Azure 订阅。

如果没有 Azure 订阅,请在开始之前创建一个免费帐户

使用 Azure Cloud Shell

Azure 托管 Azure Cloud Shell(一个可通过浏览器使用的交互式 shell 环境)。 可以将 Bash 或 PowerShell 与 Cloud Shell 配合使用来使用 Azure 服务。 可以使用 Azure Cloud Shell 预安装的命令来运行本文中的代码,而不必在本地环境中安装任何内容。

若要启动 Azure Cloud Shell,请执行以下操作:

选项 示例/链接
选择代码块右上角的“试用”。 选择“试用”不会自动将代码复制到 Cloud Shell。 Azure Cloud Shell 的“试用”示例
转到 https://shell.azure.com 或选择“启动 Cloud Shell”按钮可在浏览器中打开 Cloud Shell。 在新窗口中启动 Cloud Shell
选择 Azure 门户右上角菜单栏上的 Cloud Shell 按钮。 Azure 门户中的“Cloud Shell”按钮

若要在 Azure Cloud Shell 中运行本文中的代码,请执行以下操作:

  1. 启动 Cloud Shell。

  2. 选择代码块上的“复制”按钮以复制代码。

  3. 在 Windows 和 Linux 上选择 Ctrl+Shift+V 将代码粘贴到 Cloud Shell 会话中,或在 macOS 上选择 Cmd+Shift+V 将代码粘贴到 Cloud Shell 会话中。

  4. 选择 Enter 运行此代码。

先决条件

按照本操作方法指南操作需要:

准备开发环境

完成以下任务以准备开发环境:

  • 下载设备模拟微服务的源。
  • 下载存储适配器微服务的源。
  • 在本地运行存储适配器微服务。

本文中的说明假定使用 Windows 操作系统。 如果使用另一操作系统,则可能需要调整某些文件路径和命令以适应环境。

下载微服务

远程监视微服务 从 GitHub 下载并解压缩到本地计算机上的合适位置。 本文假设此文件夹的名称为 remote-monitoring-services-dotnet-master

从 GitHub 下载设备模拟微服务并将其解压缩到本地计算机上的适当位置。 本文假设此文件夹的名称为 device-simulation-dotnet-master

运行存储适配器微服务

在 Visual Studio Code 中打开 remote-monitoring-services-dotnet-master\storage-adapter 文件夹。 单击任意“还原”按钮,修复任何未解决的依赖项

打开 storage-adapter/WebService/appsettings.ini 文件,并将 Cosmos DB 连接字符串分配给 documentDBConnectionString 变量。

若要在本地运行微服务,请单击“调试”>“开始调试”

Visual Studio Code 中的“终端”窗口显示正在运行的微服务的输出,包括 Web 服务运行状况检查的 URL:http://127.0.0.1:9022/v1/status。 导航到此地址时,状态应显示为“正常: 活动且正常”。

在完成后续步骤时,让存储适配器微服务继续在 Visual Studio Code 的此实例中运行。

修改冷却器

在本部分中,将新的“内部温度”遥测类型添加到现有“冷却器”设备类型

  1. 在本地计算机上创建一个新文件夹 C:\temp\devicemodelsc 中

  2. 将以下文件从设备模拟微服务的已下载副本复制到新文件夹:

    目标
    Services\data\devicemodels\chiller-01.json C:\temp\devicemodels\chiller-01.json
    Services\data\devicemodels\scripts\chiller-01-state.js C:\temp\devicemodels\scripts\chiller-01-state.js
    Services\data\devicemodels\scripts\Reboot-method.js C:\temp\devicemodels\scripts\Reboot-method.js
    Services\data\devicemodels\scripts\FirmwareUpdate-method.js C:\temp\devicemodels\scripts\FirmwareUpdate-method.js
    Services\data\devicemodels\scripts\EmergencyValveRelease-method.js C:\temp\devicemodels\scripts\EmergencyValveRelease-method.js
    Services\data\devicemodels\scripts\IncreasePressure-method.js C:\temp\devicemodels\scripts\IncreasePressure-method.js
  3. 打开 C:\temp\devicemodels\chiller-01.json 文件。

  4. 在 InitialState 部分中,添加以下两个定义

    "internal_temperature": 65.0,
    "internal_temperature_unit": "F",
    
  5. Telemetry 数组中添加以下定义:

    {
      "Interval": "00:00:05",
      "MessageTemplate": "{\"internal_temperature\":${internal_temperature},\"internal_temperature_unit\":\"${internal_temperature_unit}\"}",
      "MessageSchema": {
        "Name": "chiller-internal-temperature;v1",
        "Format": "JSON",
        "Fields": {
          "temperature": "double",
          "temperature_unit": "text"
        }
      }
    },
    
  6. 保存 C:\temp\devicemodels\chiller-01.json 文件

  7. 打开 C:\temp\devicemodels\scripts\chiller-01-state.js 文件

  8. 将以下字段添加到 state 变量:

    internal_temperature: 65.0,
    internal_temperature_unit: "F",
    
  9. 按如下所示更新 main 函数

    function main(context, previousState, previousProperties) {
    
        // Restore the global state before generating the new telemetry, so that
        // the telemetry can apply changes using the previous function state.
        restoreSimulation(previousState, previousProperties);
    
        // 75F +/- 5%,  Min 25F, Max 100F
        state.temperature = vary(75, 5, 25, 100);
    
        // 70% +/- 5%,  Min 2%, Max 99%
        state.humidity = vary(70, 5, 2, 99);
    
        // 65F +/- 2%,  Min 15F, Max 125F
        state.internal_temperature = vary(65, 2, 15, 125);
    
        log("Simulation state: " + state.simulation_state);
        if (state.simulation_state === "high_pressure") {
            // 250 psig +/- 25%,  Min 50 psig, Max 300 psig
            state.pressure = vary(250, 25, 50, 300);
        } else {
            // 150 psig +/- 10%,  Min 50 psig, Max 300 psig
            state.pressure = vary(150, 10, 50, 300);
        }
    
        updateState(state);
        return state;
    }
    
  10. 保存 C:\temp\devicemodels\scripts\chiller-01-state.js 文件

创建灯泡

在本部分中,定义新的“灯泡”设备类型

  1. 创建文件 C:\temp\devicemodels\lightbulb-01.json 并添加以下内容

    {
      "SchemaVersion": "1.0.0",
      "Id": "lightbulb-01",
      "Version": "0.0.1",
      "Name": "Lightbulb",
      "Description": "Smart lightbulb device.",
      "Protocol": "MQTT",
      "Simulation": {
        "InitialState": {
          "online": true,
          "temperature": 200.0,
          "temperature_unit": "F",
          "status": "on"
        },
        "Interval": "00:00:20",
        "Scripts": [
          {
            "Type": "javascript",
            "Path": "lightbulb-01-state.js"
          }
        ]
      },
      "Properties": {
        "Type": "Lightbulb",
        "Color": "White",
        "Brightness": 75,
        "EstimatedRemainingLife": 10000
      },
      "Tags": {
        "Location": "Building 2",
        "Floor": "2",
        "Campus": "Redmond"
      },
      "Telemetry": [
        {
          "Interval": "00:00:20",
          "MessageTemplate": "{\"temperature\":${temperature},\"temperature_unit\":\"${temperature_unit}\",\"status\":\"${status}\"}",
          "MessageSchema": {
            "Name": "lightbulb-status;v1",
            "Format": "JSON",
            "Fields": {
              "temperature": "double",
              "temperature_unit": "text",
              "status": "text"
            }
          }
        }
      ],
      "CloudToDeviceMethods": {
        "SwitchOn": {
          "Type": "javascript",
          "Path": "SwitchOn-method.js"
        },
        "SwitchOff": {
          "Type": "javascript",
          "Path": "SwitchOff-method.js"
        }
      }
    }
    

    保存对 C:\temp\devicemodels\lightbulb-01.json 的更改

  2. 创建文件 C:\temp\devicemodels\scripts\lightbulb-01-state.js 并添加以下内容

    "use strict";
    
    // Default state
    var state = {
      online: true,
      temperature: 200.0,
      temperature_unit: "F",
      status: "on"
    };
    
    // Default device properties
    var properties = {};
    
    /**
     * Restore the global state using data from the previous iteration.
     *
     * @param previousState device state from the previous iteration
     * @param previousProperties device properties from the previous iteration
     */
    function restoreSimulation(previousState, previousProperties) {
      // If the previous state is null, force a default state
      if (previousState) {
        state = previousState;
      } else {
        log("Using default state");
      }
    
      if (previousProperties) {
        properties = previousProperties;
      } else {
        log("Using default properties");
      }
    }
    
    /**
     * Simple formula generating a random value around the average
     * in between min and max
     *
     * @returns random value with given parameters
     */
    function vary(avg, percentage, min, max) {
      var value = avg * (1 + ((percentage / 100) * (2 * Math.random() - 1)));
      value = Math.max(value, min);
      value = Math.min(value, max);
      return value;
    }
    
    /**
     * Simple formula that sometimes flips the status of the lightbulb
     */
    function flip(value) {
      if (Math.random() < 0.2) {
        return (value == "on") ? "off" : "on"
      }
      return value;
    }
    
    /**
     * Entry point function called by the simulation engine.
     * Returns updated simulation state.
     * Device property updates must call updateProperties() to persist.
     *
     * @param context             The context contains current time, device model and id
     * @param previousState       The device state since the last iteration
     * @param previousProperties  The device properties since the last iteration
     */
    function main(context, previousState, previousProperties) {
    
      // Restore the global device properties and the global state before
      // generating the new telemetry, so that the telemetry can apply changes
      // using the previous function state.
      restoreSimulation(previousState, previousProperties);
    
      state.temperature = vary(200, 5, 150, 250);
    
      // Make this flip every so often
      state.status = flip(state.status);
    
      updateState(state);
    
      return state;
    }
    

    保存对 C:\temp\devicemodels\scripts\lightbulb-01-state.js 的更改

  3. 创建文件 C:\temp\devicemodels\scripts\SwitchOn-method.js 并添加以下内容

    "use strict";
    
    // Default state
    var state = {
      status: "on"
    };
    
    /**
     * Entry point function called by the method.
     *
     * @param context        The context contains current time, device model and id
     * @param previousState  The device state since the last iteration
     * @param previousProperties  The device properties since the last iteration
     */
    function main(context, previousState) {
      log("Executing lightbulb Switch On method.");
      state.status = "on";
      updateState(state);
    }
    

    保存对 C:\temp\devicemodels\scripts\SwitchOn-method.js 的更改

  4. 创建文件 C:\temp\devicemodels\scripts\SwitchOff-method.js 并添加以下内容

    "use strict";
    
    // Default state
    var state = {
      status: "on"
    };
    
    /**
     * Entry point function called by the method.
     *
     * @param context        The context contains current time, device model and id
     * @param previousState  The device state since the last iteration
     * @param previousProperties  The device properties since the last iteration
     */
    function main(context, previousState) {
      log("Executing lightbulb Switch Off method.");
      state.status = "off";
      updateState(state);
    }
    

    保存对 C:\temp\devicemodels\scripts\SwitchOff-method.js 的更改

现在已创建自定义版本的“冷却器”设备类型和新的“灯泡”设备类型

测试设备

在本部分中,测试你在之前部分在本地创建的设备类型。

运行设备模拟微服务

在 Visual Studio Code 的新实例中打开从 GitHub 下载的 device-simulation-dotnet-master 文件夹。 单击任意“还原”按钮,修复任何未解决的依赖项

打开 WebService/appsettings.ini 文件,并将 Cosmos DB 连接字符串分配给 documentdb_connstring 变量,并按如下所示修改设置:

device_models_folder = C:\temp\devicemodels\

device_models_scripts_folder = C:\temp\devicemodels\scripts\

若要在本地运行微服务,请单击“调试”>“开始调试”

Visual Studio Code 中的“终端”窗口显示正在运行的微服务的输出。

在完成后续步骤时,让设备模拟微服务继续在 Visual Studio Code 的此实例中运行。

设置设备事件的监视器

在本部分中,使用 Azure CLI 设置事件监视器,以查看从连接到 IoT 中心的设备发送的遥测。

以下脚本假定 IoT 中心的名称为 device-simulation-test

# Install the IoT extension if it's not already installed
az extension add --name azure-iot

# Monitor telemetry sent to your hub
az iot hub monitor-events --hub-name device-simulation-test

测试模拟设备时,让事件监视器继续运行。

使用更新的冷却器设备类型创建模拟

在本部分中,使用 Postman 工具请求设备模拟微服务利用更新的冷却器设备类型运行模拟。 Postman 是一种可实现将 REST 请求发送到 Web 服务的工具。 所需的 Postman 配置文件位于 device-simulation-dotnet 存储库的本地副本中

要设置 Postman,请执行以下操作:

  1. 在本地计算机上打开 Postman。

  2. 单击“文件”>“导入”。 然后单击“选择文件”

  3. 导航到 device-simulation-dotnet-master/docs/postman 文件夹。 选择 Azure IoT 设备模拟解决方案accelerator.postman_collection和 Azure IoT 设备模拟解决方案accelerator.postman_environment并单击“打开”。

  4. 将“Azure IoT 设备模拟解决方案加速器”展开到可以发送的请求

  5. 单击 “无环境 ”并选择 “Azure IoT 设备模拟解决方案加速器”。

现在,已在 Postman 工作区中加载可用于与设备模拟微服务进行交互的集合和环境。

要配置和运行模拟,请执行以下操作:

  1. 在 Postman 集合中,选择“创建修改的冷却器模拟”并单击“发送”。 此请求将创建模拟的冷却器设备类型的四个实例。

  2. Azure CLI 窗口中的事件监视器输出显示来自模拟设备的遥测,包括新的 internal_temperature 值

要停止模拟,请选择 Postman 中的“停止模拟”请求并单击“发送”

使用灯泡设备类型创建模拟

在本部分中,使用 Postman 工具请求设备模拟微服务利用灯泡设备类型运行模拟。 Postman 是一种可实现将 REST 请求发送到 Web 服务的工具。

要配置和运行模拟,请执行以下操作:

  1. 在 Postman 集合中,选择“创建灯泡模拟”并单击“发送”。 此请求将创建模拟的灯泡设备类型的两个实例。

  2. Azure CLI 窗口中的事件监视器输出显示来自模拟灯泡的遥测。

要停止模拟,请选择 Postman 中的“停止模拟”请求并单击“发送”

清理资源

可在相应的 Visual Studio Code 实例中停止这两个本地运行的微服务(“调试”>“停止调试”)

如果不再需要 IoT 中心和 Cosmos DB 实例,请从 Azure 订阅中将其删除,以避免产生任何不必要的费用。

后续步骤

本指南介绍了如何创建自定义模拟设备类型,以及如何通过本地运行设备模拟微服务来对其进行测试。

建议的下一步是学习如何将自定义模拟设备类型部署到远程监视解决方案加速器