如何在 Azure 数字孪生中创建用户定义函数

重要

Azure 数字孪生服务的新版本已发布。 鉴于新服务的扩展功能,本文档集中介绍的原始 Azure 数字孪生服务 (已停用) 。

若要查看新服务的文档,请访问活动的 Azure 数字孪生文档

用户定义函数使用户能够针对传入的遥测消息和空间图形元数据配置执行的自定义逻辑。 用户还可将事件发送到预定义的终结点

本指南通过一个示例演示如何检测和警告任何超过特定温度的读数,这些读数来自接收到的温度事件。

在以下示例中,YOUR_MANAGEMENT_API_URL 代表数字孪生 API 的 URI:

https://YOUR_INSTANCE_NAME.YOUR_LOCATION.azuresmartspaces.net/management/api/v1.0
名称 替换为
YOUR_INSTANCE_NAME Azure 数字孪生实例的名称
YOUR_LOCATION 托管实例的区域

客户端库参考

如需了解用户定义函数运行时中可用作帮助程序方法的函数,请参阅客户端库参考一文。

创建匹配程序

匹配程序是图形对象,可决定要对给定遥测消息运行哪些用户定义的函数。

  • 有效匹配程序条件的比较:

    • Equals
    • NotEquals
    • Contains
  • 有效匹配程序条件的目标:

    • Sensor
    • SensorDevice
    • SensorSpace

对于数据类型值为 "Temperature" 的所有传感器遥测事件,以下示例匹配程序的计算结果都为 true。 可通过向以下对象发出经过身份验证的 HTTP POST 请求,在用户定义函数中创建多个匹配程序:

YOUR_MANAGEMENT_API_URL/matchers

使用 JSON 体:

{
  "id": "3626464-f39b-46c0-d9b0c-436aysj55",
  "name": "Temperature Matcher",
  "spaceId": "YOUR_SPACE_IDENTIFIER",
  "conditions": [
    {
      "id": "ag7gq35cfu3-e15a-4e9c-6437-sj6w68sy44s",
      "target": "Sensor",
      "path": "$.dataType",
      "value": "\"Temperature\"",
      "comparison": "Equals"
    }
  ]
}
替换为
YOUR_SPACE_IDENTIFIER 托管实例的服务器区域

创建用户定义的函数

创建用户定义的函数涉及向 Azure 数字孪生管理 API 发出多部分 HTTP 请求。

注意

多部分请求通常需要三个信息片段:

  • Content-Type 标头:
    • application/json; charset=utf-8
    • multipart/form-data; boundary="USER_DEFINED_BOUNDARY"
  • 内容处置
    • form-data; name="metadata"
  • 要上传的文件内容

Content-TypeContent-Disposition 将因使用方案而异。

可以通过 REST 客户端或诸如 Postman 的工具以编程方式(通过 C#)进行多部分请求。 各种 REST 客户端工具对复杂的多部分请求可能具有不同的支持级别。 不同工具的配置设置也可能略有不同。 请验证哪个工具最适合你的需求。

重要

向 Azure 数字孪生管理 API 发出的多部分请求通常包含两个部分:

  • Content-Type 和/或 Content-Disposition 声明的 Blob 元数据(例如关联的 MIME 类型)
  • 包括要上传的文件的非结构化内容的 Blob 内容

对于 PATCH 请求,上述两个部分都不是必需的。 对于 POST 请求或 create 操作,两者都是必需的。

采用快速入门源代码包含完整的 C# 示例,其中展示了如何针对 Azure 数字孪生管理 API 进行多部分请求。

创建匹配程序后,使用以下经过身份验证的多部分 HTTP POST 请求来上传函数代码片段:

YOUR_MANAGEMENT_API_URL/userdefinedfunctions

使用以下正文:

--USER_DEFINED_BOUNDARY
Content-Type: application/json; charset=utf-8
Content-Disposition: form-data; name="metadata"

{
  "spaceId": "YOUR_SPACE_IDENTIFIER",
  "name": "User Defined Function",
  "description": "The contents of this udf will be executed when matched against incoming telemetry.",
  "matchers": ["YOUR_MATCHER_IDENTIFIER"]
}
--USER_DEFINED_BOUNDARY
Content-Disposition: form-data; name="contents"; filename="userDefinedFunction.js"
Content-Type: text/javascript

function process(telemetry, executionContext) {
  // Code goes here.
}

--USER_DEFINED_BOUNDARY--
替换为
USER_DEFINED_BOUNDARY 多部分内容边界名称
YOUR_SPACE_IDENTIFIER 空间标识符
YOUR_MATCHER_IDENTIFIER 要使用的匹配程序的 ID
  1. 验证标头是否包括:Content-Type: multipart/form-data; boundary="USER_DEFINED_BOUNDARY"

  2. 验证正文由多个部分组成:

    • 第一部分包含所需的用户定义的函数元数据。
    • 第二部分包含 JavaScript 计算逻辑。
  3. “USER_DEFINED_BOUNDARY ”部分中,替换 spaceId () YOUR_SPACE_IDENTIFIER匹配器 (YOUR_MATCHER_IDENTIFIER) 值。

  4. 验证 JavaScript 用户定义的函数是否作为 Content-Type: text/javascript 提供。

示例函数

直接针对数据类型为 Temperature(也就是 sensor.DataType)的传感器设置传感器遥测读数:

function process(telemetry, executionContext) {

  // Get sensor metadata
  var sensor = getSensorMetadata(telemetry.SensorId);

  // Retrieve the sensor value
  var parseReading = JSON.parse(telemetry.Message);

  // Set the sensor reading as the current value for the sensor.
  setSensorValue(telemetry.SensorId, sensor.DataType, parseReading.SensorValue);
}

telemetry 参数公开了 SensorId 和 Message 属性(对应于传感器发送的消息)ExecutionContext 参数公开了以下属性:

var executionContext = new UdfExecutionContext
{
    EnqueuedTime = request.HubEnqueuedTime,
    ProcessorReceivedTime = request.ProcessorReceivedTime,
    UserDefinedFunctionId = request.UserDefinedFunctionId,
    CorrelationId = correlationId.ToString(),
};

在下一示例中,如果传感器遥测读数超过预定义的阈值,则记录消息。 如果 Azure 数字孪生实例上启用了诊断设置,则还会转发用户定义函数中的日志:

function process(telemetry, executionContext) {

  // Retrieve the sensor value
  var parseReading = JSON.parse(telemetry.Message);

  // Example sensor telemetry reading range is greater than 0.5
  if(parseFloat(parseReading.SensorValue) > 0.5) {
    log(`Alert: Sensor with ID: ${telemetry.SensorId} detected an anomaly!`);
  }
}

如果温度高于预定义常量,以下代码会触发通知:

function process(telemetry, executionContext) {

  // Retrieve the sensor value
  var parseReading = JSON.parse(telemetry.Message);

  // Define threshold
  var threshold = 70;

  // Trigger notification 
  if(parseInt(parseReading) > threshold) {
    var alert = {
      message: 'Temperature reading has surpassed threshold',
      sensorId: telemetry.SensorId,
      reading: parseReading
    };

    sendNotification(telemetry.SensorId, "Sensor", JSON.stringify(alert));
  }
}

有关更复杂的用户定义函数代码示例,请阅读 “占用”快速入门

创建角色分配

创建角色分配,以便用户定义函数依据其运行。 如果用户定义的函数不存在角色分配,则它将没有与管理 API 交互的适当权限,也没有对图形对象执行操作的权限。 用户定义的函数可以执行的操作是通过 Azure 数字孪生管理 API 中基于角色的访问控制来指定和定义的。 例如,用户定义函数可通过指定特定角色或特定访问控制路径来限制这些操作的范围。 有关详细信息,请阅读 基于角色的访问控制 文档。

  1. 查询所有角色的系统 API 以获取要分配给用户定义函数的角色 ID。 可通过发出经过身份验证的 HTTP GET 请求执行此操作,以便:

    YOUR_MANAGEMENT_API_URL/system/roles
    

    保留所需的角色 ID。 它将作为 JSON 正文属性 roleId () YOUR_DESIRED_ROLE_IDENTIFIER 传递给下面。

  2. objectId (YOUR_USER_DEFINED_FUNCTION_ID) 将是之前创建的用户定义的函数 ID。

  3. 通过查询空格fullpath来查找路径 (YOUR_ACCESS_CONTROL_PATH) 的值。

  4. 复制返回的 spacePaths 值。 稍后你将使用该值。 向以下对象发出经过身份验证的 HTTP GET 请求:

    YOUR_MANAGEMENT_API_URL/spaces?name=YOUR_SPACE_NAME&includes=fullpath
    
    替换为
    YOUR_SPACE_NAME 要使用的空间名称
  5. 将返回的 spacePaths 值粘贴到“路径”,以通过向以下对象发出经过身份验证的 HTTP POST 请求来创建用户定义的函数角色分配

    YOUR_MANAGEMENT_API_URL/roleassignments
    

    使用 JSON 体:

    {
      "roleId": "YOUR_DESIRED_ROLE_IDENTIFIER",
      "objectId": "YOUR_USER_DEFINED_FUNCTION_ID",
      "objectIdType": "YOUR_USER_DEFINED_FUNCTION_TYPE_ID",
      "path": "YOUR_ACCESS_CONTROL_PATH"
    }
    
    替换为
    YOUR_DESIRED_ROLE_IDENTIFIER 所需角色的标识符
    YOUR_USER_DEFINED_FUNCTION_ID 要使用的用户定义的函数 ID
    YOUR_USER_DEFINED_FUNCTION_TYPE_ID 指定用户定义的函数类型的 ID (UserDefinedFunctionId)
    YOUR_ACCESS_CONTROL_PATH 访问控制路径

提示

有关用户定义的函数管理 API 操作和终结点的详细信息,请参阅文章如何创建和管理角色分配

发送要处理的遥测数据

空间智能图中定义的传感器发送遥测。 反过来,遥测会触发已上传的用户定义函数的执行。 数据处理器将选取遥测数据。 然后,为调用用户定义的函数创建执行计划。

  1. 为生成读数的传感器检索匹配程序。
  2. 根据成功进行计算的匹配程序,检索相关联的用户定义函数。
  3. 执行每个用户定义函数。

后续步骤