使用 DTDL 分析器库分析和验证模型

本文介绍如何使用 .NET 分析器库分析和验证 Azure 数字孪生模型。

Azure 数字孪生中的模型是使用基于 JSON-LD 的数字孪生定义语言(DTDL)定义的。

创建模型后,建议先离线对其进行验证,然后再将其上传到 Azure 数字孪生实例。

为了帮助你验证模型,NuGet: DTDLParser 上提供了一个 .NET 客户端 DTDL 分析库。 可以直接在 C# 代码中使用分析器库。 还可以在 GitHub 的 DTDLParserResolveSample 中查看分析器的示例用法。

关于 .NET 分析程序库

DTDLParser 库提供对 DTDL 定义的模型访问权限,实质上相当于 DTDL 的 C# 反射。 此库可以独立于任何 Azure 数字孪生 SDK 使用,尤其是在视觉对象或文本编辑器中用于 DTDL 验证。 在将模型定义文件上传到服务之前,确保其有效是很有用的。

若要使用分析器库,请为其提供一组 DTDL 文档。 通常,可以从服务中检索这些模型文档,但如果客户端首先负责将其上传到服务,则也可能在本地提供它们。

下面是使用分析器的常规工作流:

  1. 从服务中检索部分或全部 DTDL 文档。
  2. 将返回的内存中 DTDL 文档传递给分析器。
  3. 分析程序验证传递给它的一组文档,并返回详细的错误信息。 此功能在编辑器方案中非常有用。
  4. 使用分析器 API 继续分析文档集中包含的模型。

分析器的功能包括:

  • 获取所有实现的模型接口(接口 extends 部分的内容)。
  • 获取模型中声明的所有属性、遥测、命令、组件和关系。 此命令还获取这些定义中包含的所有元数据,并将继承(extends 节)考虑在内。
  • 获取所有复杂的模型定义。
  • 确定一个模型是否可以从另一个模型继承。

注释

IoT 即插即用 设备使用小型语法变体来描述其功能。 此语法变体是 Azure 数字孪生中使用的 DTDL 语义上兼容的子集。 使用分析器库时,无需知道用于为数字孪生创建 DTDL 的语法变体。 默认情况下,分析程序将始终为 IoT 即插即用和 Azure 数字孪生语法返回相同的模型。

包含分析程序库的代码

可以直接将分析器库用于验证自己的应用程序中的模型或生成动态、模型驱动的 UI、仪表板和报表等内容。

若要支持以下分析器代码示例,请考虑在 Azure 数字孪生实例中定义的多个模型:

[
    {
      "@context": "dtmi:dtdl:context;3",
      "@id": "dtmi:com:contoso:coffeeMaker;1",
      "@type": "Interface",
      "contents": [
        {
          "@type": "Component",
          "name": "coffeeMaker",
          "schema": "dtmi:com:contoso:coffeeMakerInterface;1"
        }
      ]
    },
    {
      "@context": "dtmi:dtdl:context;3",
      "@id": "dtmi:com:contoso:coffeeMakerInterface;1",
      "@type": "Interface",
      "contents": [
        {
          "@type": "Property",
          "name": "waterTemp",
          "schema": "double"
        }
      ]
    },
    {
      "@context": "dtmi:dtdl:context;3",
      "@id": "dtmi:com:contoso:coffeeBar;1",
      "@type": "Interface",
      "contents": [
        {
          "@type": "Relationship",
          "name": "foo",
          "target": "dtmi:com:contoso:coffeeMaker;1"
        },
        {
          "@type": "Property",
          "name": "capacity",
          "schema": "integer"
        }
      ]
    }
  ]

以下代码演示了如何使用分析程序库在 C# 中反映这些定义的示例:

using Azure;
using Azure.DigitalTwins.Core;
using DTDLParser;
using DTDLParser.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DigitalTwins_Samples
{
    public static class ListExtensions
    {
        public static async IAsyncEnumerable<T> AsAsyncEnumerable<T>(this IEnumerable<T> input)
        {
            foreach (var value in input)
            {
                yield return value;
            }
            await Task.Yield();
        }
    }

    public class ParseModelsSample
    {
        public async Task ParseDemoAsync(DigitalTwinsClient client)
        {
            try
            {
                AsyncPageable<DigitalTwinsModelData> mdata = client.GetModelsAsync(new GetModelsOptions { IncludeModelDefinition = true });
                var models = new List<string>();
                await foreach (DigitalTwinsModelData md in mdata)
                    models.Add(md.DtdlModel);
                var parser = new ModelParser();
                IReadOnlyDictionary<Dtmi, DTEntityInfo> dtdlOM = await parser.ParseAsync(models.AsAsyncEnumerable());

                var interfaces = new List<DTInterfaceInfo>();
                IEnumerable<DTInterfaceInfo> ifenum =
                    from entity in dtdlOM.Values
                    where entity.EntityKind == DTEntityKind.Interface
                    select entity as DTInterfaceInfo;
                interfaces.AddRange(ifenum);
                foreach (DTInterfaceInfo dtif in interfaces)
                {
                    PrintInterfaceContent(dtif, dtdlOM);
                }
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"Failed due to {ex}");
                throw;
            }
        }

        public void PrintInterfaceContent(DTInterfaceInfo dtif, IReadOnlyDictionary<Dtmi, DTEntityInfo> dtdlOM, int indent = 0)
        {
            var sb = new StringBuilder();
            for (int i = 0; i < indent; i++) sb.Append("  ");
            Console.WriteLine($"{sb}Interface: {dtif.Id} | {dtif.DisplayName}");
            IReadOnlyDictionary<string, DTContentInfo> contents = dtif.Contents;

            foreach (DTContentInfo item in contents.Values)
            {
                switch (item.EntityKind)
                {
                    case DTEntityKind.Property:
                        DTPropertyInfo pi = item as DTPropertyInfo;
                        Console.WriteLine($"{sb}--Property: {pi.Name} with schema {pi.Schema}");
                        break;
                    case DTEntityKind.Relationship:
                        DTRelationshipInfo ri = item as DTRelationshipInfo;
                        Console.WriteLine($"{sb}--Relationship: {ri.Name} with target {ri.Target}");
                        break;
                    case DTEntityKind.Telemetry:
                        DTTelemetryInfo ti = item as DTTelemetryInfo;
                        Console.WriteLine($"{sb}--Telemetry: {ti.Name} with schema {ti.Schema}");
                        break;
                    case DTEntityKind.Component:
                        DTComponentInfo ci = item as DTComponentInfo;
                        Console.WriteLine($"{sb}--Component: {ci.Id} | {ci.Name}");
                        DTInterfaceInfo component = ci.Schema;
                        PrintInterfaceContent(component, dtdlOM, indent + 1);
                        break;                
                }
            }
        }
    }
}

后续步骤

编写完模型后,请参阅如何使用 Azure 数字孪生模型 API 上传模型(并执行其他管理作):