Zarządzanie grafem cyfrowych reprezentacji bliźniaczych przy użyciu relacji

Sercem usługi Azure Digital Twins jest graf bliźniaczej reprezentacji reprezentującej całe środowisko. Wykres bliźniaczej reprezentacji jest wykonany z poszczególnych cyfrowych reprezentacji bliźniaczych połączonych za pośrednictwem relacji. Ten artykuł koncentruje się na zarządzaniu relacjami i grafem jako całością; aby pracować z poszczególnymi reprezentacjami cyfrowymi, zobacz Manage digital twins (Zarządzanie cyfrowymi reprezentacjami bliźniaczymi).

Po utworzeniu działającego wystąpienia usługi Azure Digital Twins i skonfigurowaniu kodu uwierzytelniania w aplikacji klienckiej możesz tworzyć, modyfikować i usuwać cyfrowe reprezentacje bliźniacze oraz ich relacje w wystąpieniu usługi Azure Digital Twins.

Wymagania wstępne

Aby pracować z usługą Azure Digital Twins w tym artykule, musisz mieć wystąpienie usługi Azure Digital Twins i wymagane uprawnienia do korzystania z niego. Jeśli masz już skonfigurowane wystąpienie usługi Azure Digital Twins, możesz użyć tego wystąpienia i przejść do następnej sekcji. W przeciwnym razie postępuj zgodnie z instrukcjami w temacie Konfigurowanie wystąpienia i uwierzytelniania. Instrukcje zawierają informacje ułatwiające sprawdzenie, czy każdy krok został ukończony pomyślnie.

Po skonfigurowaniu wystąpienia zanotuj nazwę hosta wystąpienia. Nazwę hosta można znaleźć w witrynie Azure Portal.

Interfejsy deweloperskie

W tym artykule opisano sposób wykonywania różnych operacji zarządzania przy użyciu zestawu SDK platformy .NET (C#). Możesz również utworzyć te same wywołania zarządzania przy użyciu innych zestawów SDK języka opisanych w interfejsach API i zestawach SDK usługi Azure Digital Twins.

Inne interfejsy deweloperskie, których można użyć do wykonania tych operacji, to:

Wizualizacja

Azure Digital Twins Explorer to wizualne narzędzie do eksplorowania danych na grafie usługi Azure Digital Twins. Eksplorator umożliwia wyświetlanie, wykonywanie zapytań i edytowanie modeli, reprezentacji bliźniaczych i relacji.

Aby dowiedzieć się więcej o narzędziu Azure Digital Twins Explorer, zobacz Azure Digital Twins Explorer. Aby uzyskać szczegółowe instrukcje dotyczące korzystania z jej funkcji, zobacz Korzystanie z eksploratora usługi Azure Digital Twins.

Oto jak wygląda wizualizacja:

Screenshot of Azure Digital Twins Explorer showing sample models and twins.

Utwórz relacje

Relacje opisują, jak różne cyfrowe reprezentacje bliźniacze są ze sobą połączone, co stanowi podstawę grafu bliźniaczych reprezentacji.

Typy relacji, które można utworzyć na podstawie jednej (źródłowej) bliźniaczej reprezentacji bliźniaczej (docelowej) są definiowane jako część modelu DTDL źródłowej reprezentacji bliźniaczej. Wystąpienie relacji można utworzyć przy użyciu CreateOrReplaceRelationshipAsync() wywołania zestawu SDK z reprezentacjami bliźniaczymi i szczegółami relacji, które są zgodne z definicją języka DTDL.

Aby utworzyć relację, należy określić:

  • Identyfikator źródłowej reprezentacji bliźniaczej (srcId w poniższym przykładzie kodu): identyfikator bliźniaczej reprezentacji, z której pochodzi relacja.
  • Identyfikator docelowej reprezentacji bliźniaczej (targetId w poniższym przykładzie kodu): identyfikator bliźniaczej reprezentacji, w której dociera relacja.
  • Nazwa relacji (relName w poniższym przykładzie kodu): ogólny typ relacji, taki jak zawiera.
  • Identyfikator relacji (relId w poniższym przykładzie kodu): określona nazwa dla tej relacji, podobna do relacji Relationship1.

Identyfikator relacji musi być unikatowy w obrębie danej bliźniaczej reprezentacji źródłowej. Nie musi być globalnie unikatowa. Na przykład dla bliźniaczej reprezentacji Foo każdy określony identyfikator relacji musi być unikatowy. Jednak inny pasek bliźniaczej reprezentacji może mieć relację wychodzącą zgodną z tym samym identyfikatorem relacji Foo.

Poniższy przykładowy kod ilustruje sposób tworzenia relacji w wystąpieniu usługi Azure Digital Twins. Używa wywołania zestawu SDK (wyróżnionego) wewnątrz metody niestandardowej, która może pojawić się w kontekście większego programu.

private async static Task CustomMethod_CreateRelationshipAsync(DigitalTwinsClient client, string srcId, string targetId, string relName, IDictionary<string,object> inputProperties)
{
    var relationship = new BasicRelationship
    {
        TargetId = targetId,
        Name = relName,
        Properties = inputProperties
    };

    try
    {
        string relId = $"{srcId}-{relName}->{targetId}";
        await client.CreateOrReplaceRelationshipAsync<BasicRelationship>(srcId, relId, relationship);
        Console.WriteLine($"Created {relName} relationship successfully. Relationship ID is {relId}.");
    }
    catch (RequestFailedException rex)
    {
        Console.WriteLine($"Create relationship error: {rex.Status}:{rex.Message}");
    }

}

Teraz można wywołać tę funkcję niestandardową, aby utworzyć relację contains w następujący sposób:

await CustomMethod_CreateRelationshipAsync(client, srcId, targetId, "contains", properties);

Jeśli chcesz utworzyć wiele relacji, możesz powtarzać wywołania do tej samej metody, przekazując różne typy relacji do argumentu.

Aby uzyskać więcej informacji na temat klasy BasicRelationshippomocniczej, zobacz Interfejsy API i zestawy SDK usługi Azure Digital Twins.

Tworzenie wielu relacji między bliźniaczymi reprezentacjami

Relacje mogą być klasyfikowane jako:

  • Relacje wychodzące: relacje należące do tej reprezentacji bliźniaczej, które wskazują na zewnątrz, aby połączyć je z innymi bliźniaczymi reprezentacjami. Metoda GetRelationshipsAsync() służy do pobierania wychodzących relacji bliźniaczej reprezentacji.
  • Relacje przychodzące: relacje należące do innych reprezentacji bliźniaczych wskazujące na tę reprezentację bliźniacze w celu utworzenia łącza przychodzącego. Metoda GetIncomingRelationshipsAsync() służy do pobierania przychodzących relacji bliźniaczej reprezentacji bliźniaczej.

Nie ma żadnych ograniczeń dotyczących liczby relacji, które można mieć między dwiema reprezentacjami bliźniaczymi — możesz mieć dowolną liczbę relacji między bliźniaczymi reprezentacjami.

Oznacza to, że można wyrazić kilka różnych typów relacji między dwoma bliźniaczymi reprezentacjami jednocześnie. Na przykład bliźniacze reprezentacje A mogą mieć zarówno przechowywaną relację, jak i utworzoną relację z bliźniaczą B.

Możesz nawet utworzyć wiele wystąpień tego samego typu relacji między tymi samymi dwoma reprezentacjami bliźniaczymi, jeśli chcesz. W tym przykładzie bliźniacze reprezentacje A mogą mieć dwie różne przechowywane relacje z reprezentacją bliźniaczą B, o ile relacje mają różne identyfikatory relacji.

Uwaga

Atrybuty minMultiplicity DTDL relacji i maxMultiplicity dla nich nie są obecnie obsługiwane w usłudze Azure Digital Twins — nawet jeśli są zdefiniowane jako część modelu, nie będą wymuszane przez usługę. Aby uzyskać więcej informacji, zobacz Uwagi dotyczące języka DTDL specyficzne dla usługi.

Zbiorcze tworzenie relacji za pomocą interfejsu API importowania zadań

Interfejs API importu zadań umożliwia tworzenie wielu relacji jednocześnie w jednym wywołaniu interfejsu API. Ta metoda wymaga użycia usługi Azure Blob Storage, a także uprawnień do zapisu w wystąpieniu usługi Azure Digital Twins dla relacji i zadań zbiorczych.

Napiwek

Interfejs API zadań importu umożliwia również importowanie modeli i bliźniaczych reprezentacji w tym samym wywołaniu w celu utworzenia wszystkich części grafu jednocześnie. Aby uzyskać więcej informacji na temat tego procesu, zobacz Przekazywanie modeli, reprezentacji bliźniaczych i relacji zbiorczo za pomocą interfejsu API importowania zadań.

Aby importować relacje zbiorczo, należy utworzyć strukturę relacji (i innych zasobów uwzględnionych w zadaniu importu zbiorczego ) jako pliku NDJSON . Sekcja Relationships jest późniejsza po Twins sekcji, co czyni ją ostatnią sekcją danych grafu w pliku. Relacje zdefiniowane w pliku mogą odwoływać się do bliźniaczych reprezentacji zdefiniowanych w tym pliku lub już obecnych w wystąpieniu, a także opcjonalnie mogą obejmować inicjowanie dowolnych właściwości, które mają relacje.

Przykładowy plik importu i przykładowy projekt służą do tworzenia tych plików w temacie Wprowadzenie do interfejsu API importu zadań.

Następnie należy przekazać plik do uzupełnialnych obiektów blob w usłudze Azure Blob Storage. Aby uzyskać instrukcje dotyczące tworzenia kontenera usługi Azure Storage, zobacz Tworzenie kontenera. Następnie przekaż plik przy użyciu preferowanej metody przekazywania (niektóre opcje to polecenie AzCopy, interfejs wiersza polecenia platformy Azure lub witryna Azure Portal).

Po przekazaniu pliku NDJSON do kontenera pobierz jego adres URL w kontenerze obiektów blob. Użyjesz tej wartości w dalszej części treści wywołania interfejsu API importu zbiorczego.

Oto zrzut ekranu przedstawiający wartość adresu URL pliku obiektu blob w witrynie Azure Portal:

Screenshot of the Azure portal showing the URL of a file in a storage container.

Następnie plik można użyć w wywołaniu interfejsu API importu zadań. Podasz adres URL magazynu obiektów blob pliku wejściowego, a także nowy adres URL magazynu obiektów blob, aby wskazać, gdzie ma być przechowywany dziennik wyjściowy podczas jego tworzenia przez usługę.

Wyświetlanie listy relacji

Wyświetlanie listy właściwości pojedynczej relacji

Zawsze można deserializować dane relacji do wybranego typu. Aby uzyskać podstawowy dostęp do relacji, użyj typu BasicRelationship. Klasa BasicRelationship pomocnika zapewnia również dostęp do właściwości zdefiniowanych w relacji za pośrednictwem elementu IDictionary<string, object>. Aby wyświetlić listę właściwości, możesz użyć:

public async Task ListRelationshipProperties(DigitalTwinsClient client, string twinId, string relId, BasicDigitalTwin twin)
{

    var res = await client.GetRelationshipAsync<BasicRelationship>(twinId, relId);
    BasicRelationship rel = res.Value;
    Console.WriteLine($"Relationship Name: {rel.Name}");
    foreach (string prop in rel.Properties.Keys)
    {
        if (twin.Contents.TryGetValue(prop, out object value))
        {
            Console.WriteLine($"Property '{prop}': {value}");
        }
    }
}

Wyświetlanie listy relacji wychodzących z cyfrowej reprezentacji bliźniaczej

Aby uzyskać dostęp do listy relacji wychodzących dla danej reprezentacji bliźniaczej na wykresie, możesz użyć GetRelationships() metody w następujący sposób:

AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);

Ta metoda zwraca metodę Azure.Pageable<T> lub Azure.AsyncPageable<T>, w zależności od tego, czy używasz synchronicznej, czy asynchronicznej wersji wywołania.

Oto przykład, który pobiera listę relacji. Używa wywołania zestawu SDK (wyróżnionego) wewnątrz metody niestandardowej, która może pojawić się w kontekście większego programu.

private static async Task<List<BasicRelationship>> CustomMethod_FindOutgoingRelationshipsAsync(DigitalTwinsClient client, string dtId)
{
    // Find the relationships for the twin
    
    try
    {
        // GetRelationshipsAsync will throw if an error occurs
        AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);
        var results = new List<BasicRelationship>();
        await foreach (BasicRelationship rel in rels)
        {
            results.Add(rel);
            Console.WriteLine($"Found relationship: {rel.Id}");

            //Print its properties
            Console.WriteLine($"Relationship properties:");
            foreach(KeyValuePair<string, object> property in rel.Properties)
            {
                Console.WriteLine("{0} = {1}", property.Key, property.Value);
            }
        }

        return results;
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving relationships for {dtId} due to {ex.Message}");
        return null;
    }
}

Teraz możesz wywołać tę metodę niestandardową, aby zobaczyć relacje wychodzące bliźniaczych reprezentacji takich jak:

await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);

Możesz użyć pobranych relacji, aby przejść do innych reprezentacji bliźniaczych na grafie, odczytując target pole z zwróconej relacji i używając go jako identyfikatora następnego wywołania do GetDigitalTwin().

Wyświetlanie listy relacji przychodzących do cyfrowej reprezentacji bliźniaczej

Usługa Azure Digital Twins ma również wywołanie zestawu SDK w celu znalezienia wszystkich relacji przychodzących z daną reprezentacją bliźniaczą. Ten zestaw SDK jest często przydatny do nawigacji odwrotnej lub podczas usuwania reprezentacji bliźniaczej.

Uwaga

IncomingRelationship wywołania nie zwracają pełnej treści relacji. Aby uzyskać więcej informacji na IncomingRelationship temat klasy, zobacz dokumentację referencyjną.

Przykładowy kod w poprzedniej sekcji koncentruje się na znajdowaniu relacji wychodzących z bliźniaczej reprezentacji. Poniższy przykład ma podobną strukturę, ale zamiast tego znajduje relacje przychodzące z reprezentacją bliźniaczą. W tym przykładzie użyto również wywołania zestawu SDK (wyróżnionego) wewnątrz niestandardowej metody, która może pojawić się w kontekście większego programu.

private static async Task<List<IncomingRelationship>> CustomMethod_FindIncomingRelationshipsAsync(DigitalTwinsClient client, string dtId)
{
    // Find the relationships for the twin
    
    try
    {
        // GetRelationshipsAsync will throw an error if a problem occurs
        AsyncPageable<IncomingRelationship> incomingRels = client.GetIncomingRelationshipsAsync(dtId);

        var results = new List<IncomingRelationship>();
        await foreach (IncomingRelationship incomingRel in incomingRels)
        {
            results.Add(incomingRel);
            Console.WriteLine($"Found incoming relationship: {incomingRel.RelationshipId}");

            //Print its properties
            Response<BasicRelationship> relResponse = await client.GetRelationshipAsync<BasicRelationship>(incomingRel.SourceId, incomingRel.RelationshipId);
            BasicRelationship rel = relResponse.Value;
            Console.WriteLine($"Relationship properties:");
            foreach(KeyValuePair<string, object> property in rel.Properties)
            {
                Console.WriteLine("{0} = {1}", property.Key, property.Value);
            }
        }
        return results;
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving incoming relationships for {dtId} due to {ex.Message}");
        return null;
    }
}

Teraz możesz wywołać tę metodę niestandardową, aby zobaczyć przychodzące relacje bliźniaczych reprezentacji bliźniaczych w następujący sposób:

await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);

Wyświetlanie listy wszystkich właściwości i relacji bliźniaczych reprezentacji

Korzystając z powyższych metod wyświetlania listy relacji wychodzących i przychodzących do bliźniaczej reprezentacji bliźniaczej, można utworzyć metodę, która wyświetla pełne informacje o bliźniaczej reprezentacji, w tym właściwości bliźniaczej reprezentacji i oba typy relacji. Oto przykładowa metoda niestandardowa przedstawiająca sposób łączenia powyższych metod niestandardowych w tym celu.

private static async Task CustomMethod_FetchAndPrintTwinAsync(string twin_Id, DigitalTwinsClient client)
{
    Response<BasicDigitalTwin> res = await client.GetDigitalTwinAsync<BasicDigitalTwin>(twin_Id);
    await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);
    await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);

    return;
}

Teraz możesz wywołać tę funkcję niestandardową w następujący sposób:

await CustomMethod_FetchAndPrintTwinAsync(srcId, client);

Aktualizowanie relacji

Relacje są aktualizowane przy użyciu UpdateRelationship metody .

Uwaga

Ta metoda służy do aktualizowania właściwości relacji. Jeśli musisz zmienić bliźniacze reprezentację źródłową lub docelową reprezentację bliźniaczą relacji, musisz usunąć relację i ponownie utworzyć ją przy użyciu nowych reprezentacji bliźniaczych.

Wymagane parametry wywołania klienta to:

  • Identyfikator źródłowej reprezentacji bliźniaczej (bliźniaczej, z której pochodzi relacja).
  • Identyfikator relacji do zaktualizowania.
  • Dokument poprawki JSON zawierający właściwości i nowe wartości, które chcesz zaktualizować.

Oto przykładowy fragment kodu pokazujący sposób użycia tej metody. W tym przykładzie użyto wywołania zestawu SDK (wyróżnionego) wewnątrz niestandardowej metody, która może pojawić się w kontekście większego programu.

private async static Task CustomMethod_UpdateRelationshipAsync(DigitalTwinsClient client, string srcId, string relId, Azure.JsonPatchDocument updateDocument)
{

    try
    {
        await client.UpdateRelationshipAsync(srcId, relId, updateDocument);
        Console.WriteLine($"Successfully updated {relId}");
    }
    catch (RequestFailedException rex)
    {
        Console.WriteLine($"Update relationship error: {rex.Status}:{rex.Message}");
    }

}

Oto przykład wywołania tej metody niestandardowej, przekazując dokument poprawki JSON z informacjami, aby zaktualizować właściwość.

var updatePropertyPatch = new JsonPatchDocument();
updatePropertyPatch.AppendAdd("/ownershipUser", "ownershipUser NEW value");
await CustomMethod_UpdateRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}", updatePropertyPatch);

Usuń relacje

Pierwszy parametr określa bliźniacze reprezentację źródłową (bliźniacze, z której pochodzi relacja). Drugi parametr to identyfikator relacji. Potrzebujesz zarówno identyfikatora bliźniaczej reprezentacji, jak i identyfikatora relacji, ponieważ identyfikatory relacji są unikatowe tylko w zakresie bliźniaczej reprezentacji.

Oto przykładowy kod pokazujący, jak używać tej metody. W tym przykładzie użyto wywołania zestawu SDK (wyróżnionego) wewnątrz niestandardowej metody, która może pojawić się w kontekście większego programu.

private static async Task CustomMethod_DeleteRelationshipAsync(DigitalTwinsClient client, string srcId, string relId)
{
    try
    {
        Response response = await client.DeleteRelationshipAsync(srcId, relId);
        await CustomMethod_FetchAndPrintTwinAsync(srcId, client);
        Console.WriteLine("Deleted relationship successfully");
    }
    catch (RequestFailedException e)
    {
        Console.WriteLine($"Error {e.ErrorCode}");
    }
}

Teraz możesz wywołać tę metodę niestandardową, aby usunąć relację w następujący sposób:

await CustomMethod_DeleteRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}");

Uwaga

Jeśli chcesz usunąć wszystkie modele, reprezentacje bliźniacze i relacje w wystąpieniu jednocześnie, użyj interfejsu API usuwania zadań.

Tworzenie wielu elementów grafu jednocześnie

W tej sekcji opisano strategie tworzenia grafu z wieloma elementami jednocześnie, zamiast używania poszczególnych wywołań interfejsu API do przekazywania modeli, reprezentacji bliźniaczych i relacji w celu przekazania ich jeden po drugim.

Zbiorcze przekazywanie modeli, reprezentacji bliźniaczych i relacji za pomocą interfejsu API importowania zadań

Interfejs API importu zadań umożliwia przekazywanie wielu modeli, reprezentacji bliźniaczych i relacji do wystąpienia w jednym wywołaniu interfejsu API, co skutecznie tworzy graf jednocześnie. Ta metoda wymaga użycia usługi Azure Blob Storage, a także uprawnień do zapisu w wystąpieniu usługi Azure Digital Twins dla elementów grafu (modeli, reprezentacji bliźniaczych i relacji) i zadań zbiorczych.

Aby zaimportować zasoby zbiorczo, zacznij od utworzenia pliku NDJSON zawierającego szczegóły zasobów. Plik rozpoczyna się od Header sekcji, a następnie opcjonalnych sekcji Models, Twinsi Relationships. Nie musisz uwzględniać wszystkich trzech typów danych grafu w pliku, ale wszystkie obecne sekcje muszą być zgodne z kolejnością. Bliźniacze reprezentacje zdefiniowane w pliku mogą odwoływać się do modeli zdefiniowanych w tym pliku lub już obecnych w wystąpieniu. Opcjonalnie mogą również obejmować inicjowanie właściwości bliźniaczej reprezentacji. Relacje zdefiniowane w pliku mogą odwoływać się do bliźniaczych reprezentacji zdefiniowanych w tym pliku lub już obecnych w wystąpieniu i opcjonalnie mogą obejmować inicjowanie właściwości relacji.

Przykładowy plik importu i przykładowy projekt służą do tworzenia tych plików w temacie Wprowadzenie do interfejsu API importu zadań.

Następnie należy przekazać plik do uzupełnialnych obiektów blob w usłudze Azure Blob Storage. Aby uzyskać instrukcje dotyczące tworzenia kontenera usługi Azure Storage, zobacz Tworzenie kontenera. Następnie przekaż plik przy użyciu preferowanej metody przekazywania (niektóre opcje to polecenie AzCopy, interfejs wiersza polecenia platformy Azure lub witryna Azure Portal).

Po przekazaniu pliku NDJSON do kontenera pobierz jego adres URL w kontenerze obiektów blob. Użyjesz tej wartości w dalszej części treści wywołania interfejsu API importu zbiorczego.

Oto zrzut ekranu przedstawiający wartość adresu URL pliku obiektu blob w witrynie Azure Portal:

Screenshot of the Azure portal showing the URL of a file in a storage container.

Następnie plik można użyć w wywołaniu interfejsu API importu zadań. Podasz adres URL magazynu obiektów blob pliku wejściowego, a także nowy adres URL magazynu obiektów blob, aby wskazać, gdzie ma być przechowywany dziennik wyjściowy podczas jego tworzenia przez usługę.

Importowanie grafu za pomocą eksploratora usługi Azure Digital Twins

Azure Digital Twins Explorer to narzędzie wizualne do wyświetlania grafu bliźniaczej reprezentacji i interakcji z nim. Zawiera funkcję importowania pliku grafu w formacie JSON lub Excel, który może zawierać wiele modeli, reprezentacji bliźniaczych i relacji.

Aby uzyskać szczegółowe informacje na temat korzystania z tej funkcji, zobacz Importowanie grafu w dokumentacji eksploratora usługi Azure Digital Twins.

Tworzenie reprezentacji bliźniaczych i relacji z pliku CSV

Czasami może być konieczne utworzenie hierarchii bliźniaczych reprezentacji poza danymi przechowywanymi w innej bazie danych lub w arkuszu kalkulacyjnym lub pliku CSV. W tej sekcji pokazano, jak odczytywać dane z pliku CSV i tworzyć z niego graf bliźniaczej reprezentacji.

Rozważmy poniższą tabelę danych opisującą zestaw cyfrowych reprezentacji bliźniaczych i relacji. Modele, do których odwołuje się ten plik, muszą już istnieć w wystąpieniu usługi Azure Digital Twins.

Model ID Identyfikator bliźniaczej reprezentacji (musi być unikatowy) Nazwa relacji Identyfikator bliźniaczej reprezentacji docelowej Dane inicjowania reprezentacji bliźniaczej
dtmi:example:Floor; 1 Piętro 1 zawiera Pokój1
dtmi:example:Floor; 1 Podłoga0 zawiera Pokój0
dtmi:example:Room; 1 Pokój1 {"Temperatura": 80}
dtmi:example:Room; 1 Pokój0 {"Temperature": 70}

Jednym ze sposobów uzyskania tych danych do usługi Azure Digital Twins jest przekonwertowanie tabeli na plik CSV. Po przekonwertowaniu tabeli można napisać kod w celu zinterpretowania pliku w poleceniach w celu utworzenia reprezentacji bliźniaczych i relacji. Poniższy przykładowy kod ilustruje odczytywanie danych z pliku CSV i tworzenie grafu bliźniaczej reprezentacji w usłudze Azure Digital Twins.

W poniższym kodzie plik CSV nosi nazwę data.csv i istnieje symbol zastępczy reprezentujący nazwę hosta wystąpienia usługi Azure Digital Twins. Przykład korzysta również z kilku pakietów, które można dodać do projektu, aby ułatwić ten proces.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Azure;
using Azure.DigitalTwins.Core;
using Azure.Identity;

namespace creating_twin_graph_from_csv
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var relationshipRecordList = new List<BasicRelationship>();
            var twinList = new List<BasicDigitalTwin>();
            List<List<string>> data = ReadData();
            DigitalTwinsClient client = CreateDtClient();

            // Interpret the CSV file data, by each row
            foreach (List<string> row in data)
            {
                string modelID = row.Count > 0 ? row[0].Trim() : null;
                string srcID = row.Count > 1 ? row[1].Trim() : null;
                string relName = row.Count > 2 ? row[2].Trim() : null;
                string targetID = row.Count > 3 ? row[3].Trim() : null;
                string initProperties = row.Count > 4 ? row[4].Trim() : null;
                Console.WriteLine($"ModelID: {modelID}, TwinID: {srcID}, RelName: {relName}, TargetID: {targetID}, InitData: {initProperties}");
                var props = new Dictionary<string, object>();
                // Parse properties into dictionary (left out for compactness)
                // ...

                // Null check for source and target IDs
                if (!string.IsNullOrWhiteSpace(srcID) && !string.IsNullOrWhiteSpace(targetID) && !string.IsNullOrWhiteSpace(relName))
                {
                    relationshipRecordList.Add(
                        new BasicRelationship
                        {
                            SourceId = srcID,
                            TargetId = targetID,
                            Name = relName,
                        });
                }

                if (!string.IsNullOrWhiteSpace(srcID) && !string.IsNullOrWhiteSpace(modelID))
                twinList.Add(
                    new BasicDigitalTwin
                    {
                        Id = srcID,
                        Metadata = { ModelId = modelID },
                        Contents = props,
                    });
            }

            // Create digital twins
            foreach (BasicDigitalTwin twin in twinList)
            {
                try
                {
                    await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twin.Id, twin);
                    Console.WriteLine("Twin is created");
                }
                catch (RequestFailedException ex)
                {
                    Console.WriteLine($"Error {ex.Status}: {ex.Message}");
                }
            }

            // Create relationships between the twins
            foreach (BasicRelationship rec in relationshipRecordList)
            {
                string relId = $"{rec.SourceId}-{rec.Name}->{rec.TargetId}";
                try
                {
                    await client.CreateOrReplaceRelationshipAsync<BasicRelationship>(rec.SourceId, relId, rec);
                    Console.WriteLine($"Relationship {relId} is created");
                }
                catch (RequestFailedException ex)
                {
                    Console.WriteLine($"Error creating relationship {relId}. {ex.Status}: {ex.Message}");
                }
            }
        }

        // Method to ingest data from the CSV file
        public static List<List<string>> ReadData()
        {
            string path = "<path-to>/data.csv";
            string[] lines = System.IO.File.ReadAllLines(path);
            var data = new List<List<string>>();
            int count = 0;
            foreach (string line in lines)
            {
                if (count++ == 0)
                    continue;
                var cols = new List<string>();
                string[] columns = line.Split(',');
                foreach (string column in columns)
                {
                    cols.Add(column);
                }
                data.Add(cols);
            }
            return data;
        }

        // Method to create the digital twins client
        private static DigitalTwinsClient CreateDtClient()
        {
            string adtInstanceUrl = "https://<your-instance-hostname>";
            var credentials = new DefaultAzureCredential();
            return new DigitalTwinsClient(new Uri(adtInstanceUrl), credentials);
        }
    }
}

Przykładowy wykres bliźniaczej reprezentacji bliźniaczej z możliwością uruchamiania

Poniższy fragment kodu z możliwością uruchamiania używa operacji relacji z tego artykułu w celu utworzenia grafu bliźniaczej reprezentacji bliźniaczej z cyfrowych reprezentacji bliźniaczych i relacji.

Konfigurowanie przykładowych plików projektu

Fragment kodu używa dwóch przykładowych definicji modelu, Room.json i Floor.json. Aby pobrać pliki modelu, aby można było ich używać w kodzie, użyj tych linków, aby przejść bezpośrednio do plików w usłudze GitHub. Następnie kliknij prawym przyciskiem myszy w dowolnym miejscu na ekranie, wybierz polecenie Zapisz jako w menu prawym przyciskiem myszy przeglądarki i użyj okna Zapisz jako, aby zapisać pliki jako room.json i Floor.json.

Następnie utwórz nowy projekt aplikacji konsolowej w programie Visual Studio lub wybranym edytorze.

Następnie skopiuj następujący kod przykładu możliwego do uruchomienia do projektu:

using System;
using System.Threading.Tasks;
using System.IO;
using System.Collections.Generic;
using Azure;
using Azure.DigitalTwins.Core;
using Azure.Identity;

namespace DigitalTwins_Samples
{
    public class GraphOperationsSample
    {
        public static async Task Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            // Create the Azure Digital Twins client for API calls
            DigitalTwinsClient client = createDtClient();
            Console.WriteLine($"Service client created – ready to go");
            Console.WriteLine();

            // Upload models
            Console.WriteLine($"Upload models");
            Console.WriteLine();
            string dtdl = File.ReadAllText("<path-to>/Room.json");
            string dtdl1 = File.ReadAllText("<path-to>/Floor.json");
            var models = new List<string>
            {
                dtdl,
                dtdl1,
            };
            // Upload the models to the service
            await client.CreateModelsAsync(models);

            // Create new (Floor) digital twin
            var floorTwin = new BasicDigitalTwin();
            string srcId = "myFloorID";
            floorTwin.Metadata.ModelId = "dtmi:example:Floor;1";
            // Floor twins have no properties, so nothing to initialize
            // Create the twin
            await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(srcId, floorTwin);
            Console.WriteLine("Twin created successfully");

            // Create second (Room) digital twin
            var roomTwin = new BasicDigitalTwin();
            string targetId = "myRoomID";
            roomTwin.Metadata.ModelId = "dtmi:example:Room;1";
            // Initialize properties
            roomTwin.Contents.Add("Temperature", 35.0);
            roomTwin.Contents.Add("Humidity", 55.0);
            // Create the twin
            await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(targetId, roomTwin);
            
            // Create relationship between them
            var properties = new Dictionary<string, object>
            {
                { "ownershipUser", "ownershipUser original value" },
            };
            // <UseCreateRelationship>
            await CustomMethod_CreateRelationshipAsync(client, srcId, targetId, "contains", properties);
            // </UseCreateRelationship>
            Console.WriteLine();

            // Update relationship's Name property
            // <UseUpdateRelationship>
            var updatePropertyPatch = new JsonPatchDocument();
            updatePropertyPatch.AppendAdd("/ownershipUser", "ownershipUser NEW value");
            await CustomMethod_UpdateRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}", updatePropertyPatch);
            // </UseUpdateRelationship>
            Console.WriteLine();

            //Print twins and their relationships
            Console.WriteLine("--- Printing details:");
            Console.WriteLine($"Outgoing relationships from source twin, {srcId}:");
            // <UseFetchAndPrint>
            await CustomMethod_FetchAndPrintTwinAsync(srcId, client);
            // </UseFetchAndPrint>
            Console.WriteLine();
            Console.WriteLine($"Incoming relationships to target twin, {targetId}:");
            await CustomMethod_FetchAndPrintTwinAsync(targetId, client);
            Console.WriteLine("--------");
            Console.WriteLine();

            // Delete the relationship
            // <UseDeleteRelationship>
            await CustomMethod_DeleteRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}");
            // </UseDeleteRelationship>
            Console.WriteLine();

            // Print twins and their relationships again
            Console.WriteLine("--- Printing details (after relationship deletion):");
            Console.WriteLine("Outgoing relationships from source twin:");
            await CustomMethod_FetchAndPrintTwinAsync(srcId, client);
            Console.WriteLine();
            Console.WriteLine("Incoming relationships to target twin:");
            await CustomMethod_FetchAndPrintTwinAsync(targetId, client);
            Console.WriteLine("--------");
            Console.WriteLine();
        }

        private static DigitalTwinsClient createDtClient()
        {
            string adtInstanceUrl = "https://<your-instance-hostname>";
            var credentials = new DefaultAzureCredential();
            var client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credentials);
            return client;
        }

        // <CreateRelationshipMethod>
        private async static Task CustomMethod_CreateRelationshipAsync(DigitalTwinsClient client, string srcId, string targetId, string relName, IDictionary<string,object> inputProperties)
        {
            var relationship = new BasicRelationship
            {
                TargetId = targetId,
                Name = relName,
                Properties = inputProperties
            };

            try
            {
                string relId = $"{srcId}-{relName}->{targetId}";
                await client.CreateOrReplaceRelationshipAsync<BasicRelationship>(srcId, relId, relationship);
                Console.WriteLine($"Created {relName} relationship successfully. Relationship ID is {relId}.");
            }
            catch (RequestFailedException rex)
            {
                Console.WriteLine($"Create relationship error: {rex.Status}:{rex.Message}");
            }

        }
        // </CreateRelationshipMethod>

        // <UpdateRelationshipMethod>
        private async static Task CustomMethod_UpdateRelationshipAsync(DigitalTwinsClient client, string srcId, string relId, Azure.JsonPatchDocument updateDocument)
        {

            try
            {
                await client.UpdateRelationshipAsync(srcId, relId, updateDocument);
                Console.WriteLine($"Successfully updated {relId}");
            }
            catch (RequestFailedException rex)
            {
                Console.WriteLine($"Update relationship error: {rex.Status}:{rex.Message}");
            }

        }
        // </UpdateRelationshipMethod>

        // <FetchAndPrintMethod>
        private static async Task CustomMethod_FetchAndPrintTwinAsync(string twin_Id, DigitalTwinsClient client)
        {
            Response<BasicDigitalTwin> res = await client.GetDigitalTwinAsync<BasicDigitalTwin>(twin_Id);
            // <UseFindOutgoingRelationships>
            await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);
            // </UseFindOutgoingRelationships>
            // <UseFindIncomingRelationships>
            await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);
            // </UseFindIncomingRelationships>

            return;
        }
        // </FetchAndPrintMethod>

        // <FindOutgoingRelationshipsMethod>
        private static async Task<List<BasicRelationship>> CustomMethod_FindOutgoingRelationshipsAsync(DigitalTwinsClient client, string dtId)
        {
            // Find the relationships for the twin
            
            try
            {
                // GetRelationshipsAsync will throw if an error occurs
                // <GetRelationshipsCall>
                AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);
                // </GetRelationshipsCall>
                var results = new List<BasicRelationship>();
                await foreach (BasicRelationship rel in rels)
                {
                    results.Add(rel);
                    Console.WriteLine($"Found relationship: {rel.Id}");

                    //Print its properties
                    Console.WriteLine($"Relationship properties:");
                    foreach(KeyValuePair<string, object> property in rel.Properties)
                    {
                        Console.WriteLine("{0} = {1}", property.Key, property.Value);
                    }
                }

                return results;
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving relationships for {dtId} due to {ex.Message}");
                return null;
            }
        }
        // </FindOutgoingRelationshipsMethod>

        // <FindIncomingRelationshipsMethod>
        private static async Task<List<IncomingRelationship>> CustomMethod_FindIncomingRelationshipsAsync(DigitalTwinsClient client, string dtId)
        {
            // Find the relationships for the twin
            
            try
            {
                // GetRelationshipsAsync will throw an error if a problem occurs
                AsyncPageable<IncomingRelationship> incomingRels = client.GetIncomingRelationshipsAsync(dtId);

                var results = new List<IncomingRelationship>();
                await foreach (IncomingRelationship incomingRel in incomingRels)
                {
                    results.Add(incomingRel);
                    Console.WriteLine($"Found incoming relationship: {incomingRel.RelationshipId}");

                    //Print its properties
                    Response<BasicRelationship> relResponse = await client.GetRelationshipAsync<BasicRelationship>(incomingRel.SourceId, incomingRel.RelationshipId);
                    BasicRelationship rel = relResponse.Value;
                    Console.WriteLine($"Relationship properties:");
                    foreach(KeyValuePair<string, object> property in rel.Properties)
                    {
                        Console.WriteLine("{0} = {1}", property.Key, property.Value);
                    }
                }
                return results;
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving incoming relationships for {dtId} due to {ex.Message}");
                return null;
            }
        }
        // </FindIncomingRelationshipsMethod>

        // <DeleteRelationshipMethod>
        private static async Task CustomMethod_DeleteRelationshipAsync(DigitalTwinsClient client, string srcId, string relId)
        {
            try
            {
                Response response = await client.DeleteRelationshipAsync(srcId, relId);
                await CustomMethod_FetchAndPrintTwinAsync(srcId, client);
                Console.WriteLine("Deleted relationship successfully");
            }
            catch (RequestFailedException e)
            {
                Console.WriteLine($"Error {e.ErrorCode}");
            }
        }
        // </DeleteRelationshipMethod>
    }
}

Uwaga

Obecnie występuje znany problem dotyczący DefaultAzureCredential klasy otoki, który może spowodować błąd podczas uwierzytelniania. Jeśli wystąpi ten problem, możesz spróbować utworzyć wystąpienie DefaultAzureCredential za pomocą następującego opcjonalnego parametru, aby rozwiązać ten problem: new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true });

Aby uzyskać więcej informacji na temat tego problemu, zobacz Znane problemy usługi Azure Digital Twins.

Konfigurowanie projektu

Następnie wykonaj następujące kroki, aby skonfigurować kod projektu:

  1. Dodaj pobrane wcześniej pliki Room.json i Floor.json do projektu i zastąp <path-to> symbole zastępcze w kodzie, aby poinformować program, gdzie je znaleźć.

  2. Zastąp symbol zastępczy <your-instance-hostname> nazwą hosta wystąpienia usługi Azure Digital Twins.

  3. Dodaj dwie zależności do projektu, które będą potrzebne do pracy z usługą Azure Digital Twins. Pierwszy to pakiet zestawu SDK usługi Azure Digital Twins dla platformy .NET, a drugi udostępnia narzędzia ułatwiające uwierzytelnianie na platformie Azure.

    dotnet add package Azure.DigitalTwins.Core
    dotnet add package Azure.Identity
    

Należy również skonfigurować poświadczenia lokalne, jeśli chcesz uruchomić przykład bezpośrednio. W następnej sekcji przedstawiono ten proces.

Konfigurowanie lokalnych poświadczeń platformy Azure

W tym przykładzie użyto wartości DefaultAzureCredential (część Azure.Identity biblioteki) do uwierzytelniania użytkowników przy użyciu wystąpienia usługi Azure Digital Twins podczas uruchamiania go na komputerze lokalnym. Aby uzyskać więcej informacji na temat różnych sposobów uwierzytelniania aplikacji klienckiej za pomocą usługi Azure Digital Twins, zobacz Pisanie kodu uwierzytelniania aplikacji.

W programie DefaultAzureCredentialprzykład wyszuka poświadczenia w środowisku lokalnym, takie jak logowanie się platformy Azure w lokalnym interfejsie wiersza polecenia platformy Azure lub w programie Visual Studio lub Visual Studio Code. Z tego powodu należy zalogować się do platformy Azure lokalnie za pomocą jednego z tych mechanizmów, aby skonfigurować poświadczenia dla przykładu.

Jeśli używasz programu Visual Studio lub Visual Studio Code do uruchamiania przykładów kodu, upewnij się, że zalogowaliśmy się do tego edytora przy użyciu tych samych poświadczeń platformy Azure, których chcesz użyć do uzyskania dostępu do wystąpienia usługi Azure Digital Twins. Jeśli używasz lokalnego okna interfejsu wiersza polecenia, uruchom az login polecenie , aby zalogować się do konta platformy Azure. Następnie po uruchomieniu przykładowego kodu powinno nastąpić automatyczne uwierzytelnienie.

Uruchamianie aplikacji przykładowej

Po zakończeniu konfiguracji możesz uruchomić przykładowy projekt kodu.

Oto dane wyjściowe konsoli programu:

Screenshot of the console output showing the twin details with incoming and outgoing relationships of the twins.

Napiwek

Graf bliźniaczej reprezentacji to koncepcja tworzenia relacji między reprezentacjami bliźniaczymi. Jeśli chcesz wyświetlić wizualną reprezentację grafu bliźniaczej reprezentacji, zobacz sekcję Wizualizacja tego artykułu.

Następne kroki

Dowiedz się więcej na temat wykonywania zapytań względem grafu bliźniaczej reprezentacji bliźniaczej usługi Azure Digital Twins: