Analise e valide modelos com a biblioteca de analisadores DTDL

Este artigo descreve como analisar e validar modelos do Azure Digital Twins usando a biblioteca do analisador .NET.

Os modelos nos Gêmeos Digitais do Azure são definidos usando a linguagem DTDL (Digital Twins Definition language) baseada em JSON-LD.

Depois de criar um modelo, é recomendável validar seus modelos offline antes de carregá-los em sua instância do Azure Digital Twins.

Para ajudá-lo a validar seus modelos, uma biblioteca de análise DTDL do lado do cliente .NET é fornecida no NuGet: DTDLParser. Você pode usar a biblioteca do analisador diretamente em seu código C#. Você também pode exibir o uso de exemplo do analisador no DTDLParserResolveSample no GitHub.

Sobre a biblioteca do analisador .NET

A biblioteca DTDLParser fornece acesso de modelo às definições DTDL, atuando essencialmente como o equivalente à reflexão C# para DTDL. Essa biblioteca pode ser usada independentemente de qualquer SDK do Azure Digital Twins, especialmente para validação DTDL em um editor visual ou de texto. É útil para garantir que seus arquivos de definição de modelo sejam válidos antes de tentar carregá-los para o serviço.

Para usar a biblioteca do analisador, forneça-lhe um conjunto de documentos DTDL. Normalmente, você recuperaria esses documentos modelo do serviço, mas também poderia tê-los disponíveis localmente, se seu cliente fosse responsável por carregá-los para o serviço em primeiro lugar.

Aqui está o fluxo de trabalho geral para usar o analisador:

  1. Recupere alguns ou todos os documentos DTDL do serviço.
  2. Passe os documentos DTDL retornados na memória para o analisador.
  3. O analisador validará o conjunto de documentos passados para ele e retornará informações detalhadas de erro. Essa capacidade é útil em cenários de editor.
  4. Use as APIs do analisador para continuar analisando os modelos incluídos no conjunto de documentos.

Os recursos do analisador incluem:

  • Obtenha todas as interfaces de modelo implementadas (o conteúdo da seção da extends interface).
  • Obtenha todas as propriedades, telemetria, comandos, componentes e relações declarados no modelo. Este comando também obtém todos os metadados incluídos nessas definições e leva em conta a herança (extends seções).
  • Obtenha todas as definições de modelo complexas.
  • Determine se um modelo é atribuível a partir de outro modelo.

Nota

Os dispositivos IoT Plug and Play usam uma pequena variante de sintaxe para descrever sua funcionalidade. Essa variante de sintaxe é um subconjunto semanticamente compatível da DTDL usada nos Gêmeos Digitais do Azure. Ao usar a biblioteca do analisador, você não precisa saber qual variante de sintaxe foi usada para criar a DTDL para seu gêmeo digital. O analisador sempre retornará, por padrão, o mesmo modelo para a sintaxe IoT Plug and Play e Azure Digital Twins.

Código com a biblioteca do analisador

Você pode usar a biblioteca do analisador diretamente, para coisas como validar modelos em seu próprio aplicativo ou para gerar interface do usuário, painéis e relatórios dinâmicos e orientados por modelos.

Para dar suporte ao exemplo de código do analisador abaixo, considere vários modelos definidos em uma instância do Azure Digital Twins:

[
    {
      "@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"
        }
      ]
    }
  ]

O código a seguir mostra um exemplo de como usar a biblioteca do analisador para refletir sobre essas definições em 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;                
                }
            }
        }
    }
}

Próximos passos

Quando terminar de escrever seus modelos, veja como carregá-los (e fazer outras operações de gerenciamento) com as APIs de Modelos de Gêmeos Digitais do Azure: