Análisis y validación de modelos con la biblioteca del analizador de DTDL

En este artículo se describe cómo analizar y validar modelos de Azure Digital Twins mediante la biblioteca del analizador de .NET.

Los modelos de Azure Digital Twins se definen con el lenguaje de definición de gemelos digitales (DTDL) basado en JSON-LD.

Después de crear un modelo, se recomienda validar los modelos sin conexión antes de cargarlos en la instancia de Azure Digital Twins.

Para ayudarle a validar los modelos, se proporciona una biblioteca de análisis de DTDL del lado cliente de .NET en NuGet: DTDLParser. Puede usar la biblioteca del analizador directamente en el código de C#. También puede ver el uso de ejemplo del analizador en DTDLParserResolveSample en GitHub.

Acerca de la biblioteca del analizador de .NET

La biblioteca DTDLParser proporciona acceso de modelo a las definiciones de DTDL, actuando básicamente como el equivalente de la reflexión de C# para DTDL. Esta biblioteca se puede usar independientemente de cualquier SDK de Azure Digital Twins, en especial para la validación de DTDL en un editor de texto o visual. Es útil para asegurarse de que los archivos de definición del modelo sean válidos antes de intentar cargarlos en el servicio.

Para usar la biblioteca del analizador, se le proporciona un conjunto de documentos DTDL. Normalmente, estos documentos de modelo se recuperan del servicio, pero también pueden estar disponibles localmente, si el cliente fue responsable de cargarlos en el servicio en primer lugar.

Este es el flujo de trabajo general para usar el analizador:

  1. Recupere algunos o todos los documentos DTDL del servicio.
  2. Pase los documentos DTDL en memoria devueltos al analizador.
  3. El analizador validará el conjunto de documentos que se le pasa y devolverá información detallada sobre el error. Esta capacidad es útil en escenarios de editor.
  4. Use las API del analizador para seguir analizando los modelos incluidos en el conjunto de documentos.

Entre las capacidades del analizador se incluyen:

  • Obtener todas las interfaces del modelo implementadas (el contenido de la sección extends de la interfaz).
  • Obtener todas las propiedades, la telemetría, los comandos, los componentes y las relaciones declaradas en el modelo. Este comando también obtiene todos los metadatos incluidos en estas definiciones y tiene en cuenta la herencia (secciones extends).
  • Obtener todas las definiciones de modelos complejos.
  • Determinar si un modelo es asignable desde otro modelo.

Nota:

Los dispositivosIoT Plug and Play usan una pequeña variante de la sintaxis para describir su funcionalidad. Esta variante de sintaxis es un subconjunto compatible semánticamente del DTDL que se usa en Azure Digital Twins. Cuando se usa la biblioteca del analizador, no es necesario saber qué variante de sintaxis se ha usado para crear el DTDL para el gemelo digital. El analizador siempre devolverá, de manera predeterminada, el mismo modelo para la sintaxis de IoT Plug and Play y de Azure Digital Twins.

Código con la biblioteca del analizador

Puede usar la biblioteca del analizador directamente para, por ejemplo, la validación de modelos en su propia aplicación o la generación de interfaces de usuario, paneles e informes dinámicos controlados por modelos.

Para admitir el ejemplo de código del analizador siguiente, examine varios modelos definidos en una instancia de 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"
        }
      ]
    }
  ]

En el código siguiente se muestra un ejemplo de cómo usar la biblioteca del analizador para reflejar estas definiciones en 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;                
                }
            }
        }
    }
}

Pasos siguientes

Una vez que haya terminado de escribir los modelos, consulte cómo cargarlos (y realizar otras operaciones de administración) con las API de modelos de Azure Digital Twins: