Parsa och validera modeller med DTDL-parserbiblioteket

Den här artikeln beskriver hur du parsar och validerar Azure Digital Twins-modeller med hjälp av .NET-parserbiblioteket.

Modeller i Azure Digital Twins definieras med hjälp av det JSON-LD-baserade DTDL-språket (Digital Twins Definition Language).

När du har skapat en modell rekommenderar vi att du verifierar dina modeller offline innan du laddar upp dem till din Azure Digital Twins-instans.

För att hjälpa dig att verifiera dina modeller finns ett DTDL-parsningsbibliotek på .NET-klientsidan på NuGet: DTDLParser. Du kan använda parsningsbiblioteket direkt i C#-koden. Du kan också visa exempelanvändningen av parsern i DTDLParserResolveSample i GitHub.

Om .NET-parsningsbiblioteket

DTDLParser-biblioteket ger modellåtkomst till DTDL-definitionerna, vilket i huvudsak fungerar som motsvarigheten till C#-reflektion för DTDL. Det här biblioteket kan användas oberoende av Azure Digital Twins SDK, särskilt för DTDL-validering i ett visuellt objekt eller textredigerare. Det är användbart för att se till att modelldefinitionsfilerna är giltiga innan du försöker ladda upp dem till tjänsten.

Om du vill använda parserbiblioteket tillhandahåller du en uppsättning DTDL-dokument. Vanligtvis hämtar du dessa modelldokument från tjänsten, men du kan också ha dem tillgängliga lokalt, om klienten var ansvarig för att ladda upp dem till tjänsten från början.

Här är det allmänna arbetsflödet för att använda parsern:

  1. Hämta vissa eller alla DTDL-dokument från tjänsten.
  2. Skicka de returnerade, minnesinterna DTDL-dokumenten till parsern.
  3. Parsern verifierar den uppsättning dokument som skickas till den och returnerar detaljerad felinformation. Den här möjligheten är användbar i redigeringsscenarier.
  4. Använd parser-API:erna för att fortsätta analysera de modeller som ingår i dokumentuppsättningen.

Funktionerna i parsern är:

  • Hämta alla implementerade modellgränssnitt (innehållet i gränssnittets extends avsnitt).
  • Hämta alla egenskaper, telemetri, kommandon, komponenter och relationer som deklarerats i modellen. Det här kommandot hämtar även alla metadata som ingår i dessa definitioner och tar hänsyn till arv (extends avsnitt).
  • Hämta alla komplexa modelldefinitioner.
  • Avgör om en modell kan tilldelas från en annan modell.

Kommentar

IoT Plug and Play-enheter använder en liten syntaxvariant för att beskriva sina funktioner. Den här syntaxvarianten är en semantiskt kompatibel delmängd av DTDL som används i Azure Digital Twins. När du använder parsningsbiblioteket behöver du inte veta vilken syntaxvariant som användes för att skapa DTDL för din digitala tvilling. Parsern returnerar alltid som standard samma modell för både IoT Plug and Play- och Azure Digital Twins-syntaxen.

Koda med parsningsbiblioteket

Du kan använda parsningsbiblioteket direkt för saker som att verifiera modeller i ditt eget program eller för att generera dynamiskt, modelldrivet användargränssnitt, instrumentpaneler och rapporter.

För att stödja parser-kodexemplet nedan bör du överväga flera modeller som definierats i en Azure Digital Twins-instans:

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

Följande kod visar ett exempel på hur du använder parser-biblioteket för att reflektera över dessa definitioner i 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;                
                }
            }
        }
    }
}

Nästa steg

När du är klar med att skriva dina modeller kan du se hur du laddar upp dem (och utför andra hanteringsåtgärder) med Azure Digital Twins Models-API:erna: