이 문서에서는 .NET 파서 라이브러리를 사용하여 Azure Digital Twins 모델을 구문 분석하고 유효성을 검사하는 방법을 설명합니다.
Azure Digital Twins의 모델은 JSON 기반 DTDL(Digital Twins 정의 언어)을 사용하여 정의됩니다.
모델을 만든 후에는 모델을 Azure Digital Twins 인스턴스에 업로드하기 전에 오프라인으로 모델의 유효성을 검사하는 것이 좋습니다.
모델의 유효성을 검사하는 데 도움이 되도록 NuGet: DTDLParser에 .NET 클라이언트 측 DTDL 구문 분석 라이브러리가 제공됩니다. C# 코드에서 직접 파서 라이브러리를 사용할 수 있습니다. GitHub의 DTDLParserResolveSample에서 파서의 샘플 사용을 볼 수도 있습니다.
.NET 파서 라이브러리 정보
DTDLParser 라이브러리는 기본적으로 DTDL에 대한 C# 리플렉션과 동등한 역할을 하는 DTDL 정의에 대한 모델 액세스를 제공합니다. 이 라이브러리는 Azure Digital Twins SDK와 독립적으로 사용할 수 있으며, 특히 시각적 개체 또는 텍스트 편집기의 DTDL 유효성 검사에 사용할 수 있습니다. 서비스에 업로드하기 전에 모델 정의 파일이 유효한지 확인하는 데 유용합니다.
파서 라이브러리를 사용하려면 DTDL 문서 집합을 제공합니다. 일반적으로 서비스에서 이러한 모델 문서를 검색하지만 클라이언트가 처음에 서비스에 업로드할 책임이 있는 경우 로컬에서 사용할 수 있도록 할 수도 있습니다.
파서 사용에 대한 일반적인 워크플로는 다음과 같습니다.
- 서비스에서 일부 또는 모든 DTDL 문서를 검색합니다.
- 반환된 메모리 내 DTDL 문서를 파서에 전달합니다.
- 파서는 전달된 문서 집합의 유효성을 검사하고 자세한 오류 정보를 반환합니다. 이 기능은 편집기 시나리오에서 유용합니다.
- 파서 API를 사용하여 문서 집합에 포함된 모델을 계속 분석합니다.
파서의 기능은 다음과 같습니다.
- 구현된 모든 모델 인터페이스(인터페이스 섹션의
extends내용)를 가져옵니다. - 모델에 선언된 모든 속성, 원격 분석, 명령, 구성 요소 및 관계를 가져옵니다. 또한 이 명령은 이러한 정의에 포함된 모든 메타데이터를 가져오고 상속(
extends섹션)을 고려합니다. - 모든 복잡한 모델 정의를 가져옵니다.
- 모델을 다른 모델에서 할당할 수 있는지 여부를 확인합니다.
비고
IoT 플러그 앤 플레이 디바이스는 작은 구문 변형을 사용하여 해당 기능을 설명합니다. 이 구문 변형은 Azure Digital Twins에서 사용되는 DTDL의 의미상 호환되는 하위 집합입니다. 파서 라이브러리를 사용하는 경우 디지털 트윈에 대한 DTDL을 만드는 데 사용된 구문 변형을 알 필요가 없습니다. 파서는 항상 기본적으로 IoT 플러그 앤 플레이와 Azure Digital Twins 구문 모두에 대해 동일한 모델을 반환합니다.
파서 라이브러리를 사용하는 코드
파서 라이브러리를 직접 사용하여 애플리케이션에서 모델의 유효성을 검사하거나 동적 모델 기반 UI, 대시보드 및 보고서를 생성할 수 있습니다.
다음 파서 코드 예제를 지원하려면 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"
}
]
}
]
다음 코드는 파서 라이브러리를 사용하여 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;
}
}
}
}
}
다음 단계
모델 작성이 완료되면 Azure Digital Twins 모델 API를 사용하여 모델을 업로드하고 다른 관리 작업을 수행하는 방법을 참조하세요.