Condividi tramite


Analizzare e convalidare i modelli con la libreria di parser DTDL

Questo articolo descrive come analizzare e convalidare i modelli di Gemelli digitali di Azure usando la libreria parser .NET.

I modelli in Gemelli digitali di Azure vengono definiti usando il linguaggio DTDL (Digital Twins Definition) basato su JSON-LD.

Dopo aver creato un modello, è consigliabile convalidare i modelli offline prima di caricarli nell'istanza di Gemelli digitali di Azure.

Per convalidare i modelli, viene fornita una libreria di analisi DTDL sul lato client .NET in NuGet: DTDLParser. È possibile usare la libreria parser direttamente nel codice C#. È anche possibile visualizzare l'uso di esempio del parser in DTDLParserResolveSample in GitHub.

Informazioni sulla libreria parser .NET

La libreria DTDLParser fornisce l'accesso del modello alle definizioni DTDL, fungendo essenzialmente da equivalente della reflection C# per DTDL. Questa libreria può essere usata indipendentemente da qualsiasi SDK di Gemelli digitali di Azure, in particolare per la convalida DTDL in un editor visivo o di testo. È utile per assicurarsi che i file di definizione del modello siano validi prima di provare a caricarli nel servizio.

Per utilizzare la libreria parser, è necessario fornirle un set di documenti DTDL. In genere, questi documenti del modello vengono recuperati dal servizio, ma potrebbero anche essere disponibili localmente, se il cliente è stato responsabile del caricamento nel servizio in primo luogo.

Di seguito è riportato il flusso di lavoro generale per l'utilizzo del parser:

  1. Recuperare alcuni o tutti i documenti DTDL dal servizio.
  2. Passare i documenti DTDL in memoria restituiti al parser.
  3. Il parser convalida il set di documenti passati e restituisce informazioni dettagliate sull'errore. Questa funzionalità è utile negli scenari dell'editor.
  4. Usare le API del parser per continuare ad analizzare i modelli inclusi nel set di documenti.

Le funzionalità del parser includono:

  • Ottenere tutte le interfacce del modello implementate (il contenuto della sezione dell'interfaccia extends ).
  • Ottenere tutte le proprietà, i dati di telemetria, i comandi, i componenti e le relazioni dichiarati nel modello. Questo comando ottiene anche tutti i metadati inclusi in queste definizioni e tiene conto dell'ereditarietà (extends sezioni).
  • Ottenere tutte le definizioni di modelli complessi.
  • Determinare se un modello è assegnabile da un altro modello.

Annotazioni

I dispositivi Plug and Play IoT usano una piccola variante di sintassi per descriverne la funzionalità. Questa variante di sintassi è un subset semanticamente compatibile della DTDL usata in Gemelli digitali di Azure. Quando si usa la libreria parser, non è necessario sapere quale variante di sintassi è stata usata per creare la DTDL per il gemello digitale. Per impostazione predefinita, il parser restituirà sempre lo stesso modello per la sintassi Plug and Play IoT e Gemelli digitali di Azure.

Codifica con la libreria parser

È possibile usare direttamente la libreria parser, ad esempio per la convalida dei modelli nell'applicazione o per la generazione di interfaccia utente, dashboard e report dinamici basati su modello.

Per supportare l'esempio di codice parser seguente, prendere in considerazione diversi modelli definiti in un'istanza di Gemelli digitali di 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"
        }
      ]
    }
  ]

Nel codice seguente viene illustrato un esempio di come usare la libreria parser per riflettere su queste definizioni in 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;                
                }
            }
        }
    }
}

Passaggi successivi

Al termine della scrittura dei modelli, vedere come caricarli (ed eseguire altre operazioni di gestione) con le API dei modelli di Gemelli digitali di Azure: