Share via


Analisar e validar modelos com a biblioteca do analisador de DTDL

Este artigo descreve como analisar e validar modelos de Gêmeos Digitais do Azure usando a biblioteca de analisadores .NET.

Os modelos nos Gêmeos Digitais do Azure são definidos usando a DTDL (Linguagem de Definição de Gêmeos Digitais) baseada em JSON-LD.

Depois de criar um modelo, é recomendável validar seus modelos offline antes de carregá-los na instância dos Gêmeos Digitais do Azure.

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 de analisadores .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 dos Gêmeos Digitais do Azure, especialmente para validação de DTDL em um editor de texto ou visual. Ela é útil para verificar se os arquivos de definição de modelo são válidos antes de tentar carregá-los no serviço.

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

Este é o fluxo de trabalho geral para usar o analisador:

  1. Recupere alguns ou todos os documentos de DTDL do serviço.
  2. Passe os documentos de DTDL retornados e na memória para o analisador.
  3. O analisador validará o conjunto de documentos passados para ele e retornará informações detalhadas sobre o 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.

As funcionalidades do analisador incluem:

  • Obter todas as interfaces de modelo implementadas (o conteúdo da seção extends da interface).
  • Obter todas as propriedades, telemetria, comandos, componentes e relações declaradas no modelo. Esse comando também obtém todos os metadados incluídos nessas definições e usa a herança (seções extends) na conta.
  • Obter todas as definições de modelo complexas.
  • Determinar se um modelo pode ser atribuído de outro modelo.

Observação

Os dispositivos IoT Plug and Play usam uma variante de sintaxe pequena para descrever sua funcionalidade. Essa variante de sintaxe é um subconjunto compatível semanticamente da DTDL que é usado 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 do seu gêmeo digital. O analisador sempre retornará, por padrão, o mesmo modelo para a sintaxe IoT Plug and Play e dos Gêmeos Digitais do Azure.

Código com a biblioteca do analisador

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

Para dar suporte ao exemplo de código do analisador abaixo, considere vários modelos definidos em uma instância dos Gêmeos Digitais do 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"
        }
      ]
    }
  ]

O seguinte código 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óximas etapas

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: