Управление цифровыми двойниками

Сущности в среде представлены цифровыми двойниками. Управление цифровыми двойниками может включать создание, изменение и удаление.

Эта статья посвящена управлению цифровыми двойниками. Сведения о работе со связями и графом двойника в целом см. в статье об управлении графом двойника со связями.

Совет

Все функции пакета SDK имеют синхронные и асинхронные версии.

Необходимые компоненты

Для работы с Azure Digital Twins в этой статье потребуется экземпляр Azure Digital Twins и необходимые разрешения для его использования. Если у вас уже настроен экземпляр Azure Digital Twins, этот экземпляр можно использовать и перейти к следующему разделу. В противном случае выполните инструкции по настройке экземпляра и аутентификации. В инструкциях приводится информация, которая поможет вам убедиться, что вы успешно выполнили каждый шаг.

После настройки экземпляра запишите имя узла экземпляра. Вы можете найти имя хоста на портале Azure.

Интерфейсы для разработчиков

В этой статье объясняется, как выполнять различные операции управления с помощью .NET (C#) SDK. Вы также можете создавать те же вызовы управления с помощью пакетов SDK для других языков, описанных в статье, посвященной API и пакетам SDK для Azure Digital Twins.

Вот некоторые другие интерфейсы для разработчиков, которые можно использовать для выполнения этих операций:

Визуализация

Azure Digital Twins Explorer — это визуальное средство для изучения данных в графе Azure Digital Twins. С помощью этого обозревателя можно просматривать, запрашивать и изменять модели, двойников и отношения.

Сведения о средстве Azure Digital Twins Explorer см. в статье Azure Digital Twins Explorer. Подробные инструкции по использованию его функций см. в статье Использование Azure Digital Twins Explorer.

Вот так выглядят визуализации:

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

Создание цифрового двойника

Чтобы создать двойник, используйте метод CreateOrReplaceDigitalTwinAsync() в клиенте службы, как показано ниже.

await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twinId, initData);

Чтобы создать цифровой двойник, необходимо указать:

  • Значение идентификатора, которое необходимо назначить цифровому двойнику (вы определяете этот идентификатор при создании двойника)
  • модель, которую вы хотите использовать.
  • Любая требуемая инициализация данных двойника, включая...
    • Свойства (необязательное инициализация): можно задать начальные значения для свойств цифрового двойника, если требуется. Свойства обрабатываются как необязательные и могут быть заданы позже, но обратите внимание, что они не будут отображаться как часть двойника, пока они не будут заданы.
    • Компоненты (инициализация требуется, если они присутствуют в двойниках): если двойник содержит все компоненты, их необходимо инициализировать при создании двойника. Они могут быть пустыми объектами, но сами компоненты должны существовать.

Модель и все начальные значения свойств предоставляются с помощью параметра initData, который представляет собой строку JSON, содержащую соответствующие данные. Чтобы получить дополнительные сведения о структурировании этого объекта, перейдите к следующему разделу.

Совет

После создания или обновления двойника может возникнуть задержка до 10 секунд, прежде чем изменения будут отражены в запросах. Эта задержка не возникает в API GetDigitalTwin (описанном далее в этой статье), поэтому, если вы хотите сразу же увидеть созданные двойники, используйте вызов API вместо запроса.

Инициализация модели и свойств

Свойства двойника можно инициализировать во время его создания.

API создания двойника принимает объект, который сериализуется в допустимое описание JSON свойств двойника. Описание формата JSON для двойника см. в статье о цифровых двойниках и графе двойника.

Сначала можно создать объект данных для представления двойника и данных его свойств. Можно создать объект параметра вручную или с помощью предоставленного вспомогательного класса. Ниже приведен пример каждого из них.

Создание двойников с помощью данных, созданных вручную

Без использования каких-либо настраиваемых вспомогательных классов можно представить свойства двойника в Dictionary<string, object>, где string — это имя свойства, а object — это объект, представляющий свойство и его значение.

// Define a custom model type for the twin to be created

internal class CustomDigitalTwin
{
    [JsonPropertyName(DigitalTwinsJsonPropertyNames.DigitalTwinId)]
    public string Id { get; set; }

    [JsonPropertyName(DigitalTwinsJsonPropertyNames.DigitalTwinETag)]
    public string ETag { get; set; }

    [JsonPropertyName("temperature")]
    public double Temperature { get; set; }

    [JsonPropertyName("humidity")]
    public double Humidity{ get; set; }
}

// Initialize properties and create the twin
public class TwinOperationsCreateTwin
{
    public async Task CreateTwinAsync(DigitalTwinsClient client)
    {
        // Initialize the twin properties
        var myTwin = new CustomDigitalTwin
        {
            Temperature = 25.0,
            Humidity = 50.0,
        };

        // Create the twin
        const string twinId = "<twin-ID>";
        Response<CustomDigitalTwin> response = await client.CreateOrReplaceDigitalTwinAsync(twinId, myTwin);
        Console.WriteLine($"Temperature value: {response.Value.Temperature}");
    }
}

Создание двойников с помощью вспомогательного класса

Вспомогательный класс BasicDigitalTwin позволяет хранить поля свойств непосредственно в объекте двойника. Возможно, вам по-прежнему нужно создать список свойств с помощью Dictionary<string, object>объекта двойника, который затем можно добавить в объект двойника как его CustomProperties напрямую.

string twinId = "myTwinID";
var initData = new BasicDigitalTwin
{
    Id = twinId,
    Metadata = { ModelId = "dtmi:example:Room;1" },
    // Initialize properties
    Contents =
    {
        { "Temperature", 25.0 },
        { "Humidity", 50.0 },
    },
};

await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twinId, initData);

Примечание.

BasicDigitalTwin объекты содержат поле Id. Это поле можно оставить пустым, но, если вы добавляете значение идентификатора, оно должно соответствовать параметру идентификатора, переданному вызову метода CreateOrReplaceDigitalTwinAsync(). Например:

twin.Id = "myRoomId";

Создание двойников в массовом режиме с помощью API импорта заданий

API импорта заданий можно использовать для создания нескольких двойников одновременно в одном вызове API. Этот метод требует использования Хранилище BLOB-объектов Azure и разрешений на запись в экземпляре Azure Digital Twins для двойников и массовых заданий.

Совет

API заданий импорта также позволяет импортировать модели и связи в одном вызове для создания всех частей графа одновременно. Дополнительные сведения об этом процессе см. в статье "Отправка моделей, двойников и связей" в массовом режиме с помощью API импорта заданий.

Для массового импорта двойников необходимо структурировать двойники (и любые другие ресурсы, включенные в задание массового импорта) в виде файла NDJSON . Раздел Twins приходит после Models раздела (и перед разделом Relationships ). Двойники, определенные в файле, могут ссылаться на модели, определенные в этом файле или уже присутствующих в экземпляре, и при необходимости они могут включать инициализацию свойств двойника.

Вы можете просмотреть пример файла импорта и пример проекта для создания этих файлов в введение в API заданий импорта.

Затем файл необходимо передать в добавочный большой двоичный объект в Хранилище BLOB-объектов Azure. Инструкции по созданию контейнера хранилища Azure см. в статье "Создание контейнера". Затем отправьте файл с помощью предпочтительного метода отправки (некоторые параметры — команда AzCopy, Azure CLI или портал Azure).

После отправки файла NDJSON в контейнер получите ЕГО URL-адрес в контейнере BLOB-объектов . Это значение будет использоваться позже в тексте вызова API массового импорта.

Ниже приведен снимок экрана, показывающий значение URL-адреса файла БОЛЬШОго двоичного объекта в портал Azure:

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

Затем файл можно использовать в вызове API импорта заданий. Вы предоставляете URL-адрес хранилища BLOB-объектов входного файла и новый URL-адрес хранилища BLOB-объектов, чтобы указать, где должен храниться выходной журнал после создания службы.

Получение данных для цифрового двойника

Чтобы получить доступ к сведениям о любом цифровом двойнике, вызовите метод GetDigitalTwin(), как показано ниже.

Response<BasicDigitalTwin> twinResponse = await client.GetDigitalTwinAsync<BasicDigitalTwin>(twinId);
twin = twinResponse.Value;

Этот вызов возвращает данные двойника строго типизированного типа объекта, такого как BasicDigitalTwin. BasicDigitalTwin — вспомогательный класс сериализации, включенный в пакет SDK, который возвращает метаданные и свойства основных двойников в предварительно подготовленных формах. Вы всегда можете десериализовать данные двойника, используя библиотеку JSON по своему усмотрению, например System.Text.Json или Newtonsoft.Json. Для базового доступа к двойнику очень удобно использовать вспомогательные классы.

Примечание.

BasicDigitalTwin использует атрибуты System.Text.Json. Чтобы использовать BasicDigitalTwin с DigitalTwinsClient, необходимо либо инициализировать клиент с помощью конструктора по умолчанию, либо, если вы хотите настроить параметр сериализатора, использовать JsonObjectSerializer.

Вспомогательный класс BasicDigitalTwin также предоставляет доступ к свойствам, определенным в двойнике, через Dictionary<string, object>. Чтобы вывести список свойств двойника, можно использовать следующий код:

BasicDigitalTwin twin;
Response<BasicDigitalTwin> twinResponse = await client.GetDigitalTwinAsync<BasicDigitalTwin>(twinId);
twin = twinResponse.Value;
Console.WriteLine($"Model id: {twin.Metadata.ModelId}");
foreach (string prop in twin.Contents.Keys)
{
    if (twin.Contents.TryGetValue(prop, out object value))
        Console.WriteLine($"Property '{prop}': {value}");
}

При получении двойника с помощью метода GetDigitalTwin() возвращаются только те свойства, которые были заданы хотя бы один раз.

Совет

displayName для двойника является частью метаданных модели, поэтому не будет отображаться при получении данных для экземпляра двойника. Чтобы увидеть это значение, можно извлечь его из модели.

Чтобы получить несколько двойников с помощью одного вызова API, ознакомьтесь с примерами API запросов в графе двойников.

Рассмотрим следующую модель (написанную на языке определения цифровых двойников (DTDL)), которая определяет цифровой двойник Moon.

{
    "@id": "dtmi:example:Moon;1",
    "@type": "Interface",
    "@context": "dtmi:dtdl:context;3",
    "contents": [
        {
            "@type": "Property",
            "name": "radius",
            "schema": "double",
            "writable": true
        },
        {
            "@type": "Property",
            "name": "mass",
            "schema": "double",
            "writable": true
        }
    ]
}

Результат вызова object result = await client.GetDigitalTwinAsync("my-moon"); двойника типа Луны может выглядеть следующим образом:

{
  "$dtId": "myMoon-001",
  "$etag": "W/\"e59ce8f5-03c0-4356-aea9-249ecbdc07f9\"",
  "radius": 1737.1,
  "mass": 0.0734,
  "$metadata": {
    "$model": "dtmi:example:Moon;1",
    "radius": {
      "lastUpdateTime": "2022-12-06T20:00:32.8209188Z"
    },
    "mass": {
      "lastUpdateTime": "2022-12-04T12:04:43.3859361Z"
    }
  }
}

Определенные свойства цифрового двойника возвращаются в виде свойств верхнего уровня. Метаданные или системные сведения, которые не являются частью $ определения DTDL, возвращаются с префиксом. Свойства метаданных содержат следующие значения.

  • $dtId — идентификатор цифрового двойника в этом экземпляре Azure Digital Twins.
  • $etag — стандартное поле HTTP, назначенное веб-сервером. Это значение меняется на новое при каждом обновлении двойника, что может быть полезно для определения того, были ли данные двойника обновлены на сервере с момента предыдущей проверки. С помощью свойства If-Match можно выполнять операции обновления и удаления, которые завершаются только в том случае, если ETag сущности совпадает с указанным ETag. Дополнительные сведения об этих операциях см. в документации по операциям обновления DigitalTwins и удаления DigitalTwins.
  • $metadata: набор свойств метаданных, которые могут включать в себя следующее:
    • $model, DTMI модели цифрового двойника.
    • lastUpdateTime для свойств двойника. Это метка времени, указывающая дату и время обработки сообщения об обновлении свойства Azure Digital Twins.
    • sourceTime для свойств двойника. Это необязательное свойство, представляющее метку времени при обнаружении обновления свойства в реальном мире.

Дополнительные сведения о полях, содержащихся в цифровом двойнике в формате JSON цифрового двойника. Дополнительные сведения о вспомогательных классах сериализации, таких как BasicDigitalTwin, см. в статье об API и пакетах SDK для Azure Digital Twins.

Просмотр всех цифровых двойников

Чтобы просмотреть все цифровые двойники в своем экземпляре, воспользуйтесь запросом. Его можно выполнитьAPI запросов или команд CLI.

Ниже приведен текст базового запроса, который возвращает список всех цифровых двойников в экземпляре:

SELECT * FROM DIGITALTWINS

Обновление цифрового двойника

Чтобы обновить свойства цифрового двойника, напишите сведения, которые необходимо заменить в формате исправления JSON. Полный список операций исправления JSON, которые можно использовать, включая replaceadd и remove, см. в разделе "Операции для исправления JSON".

После создания документа исправления JSON, содержащего сведения об обновлении, передайте документ в UpdateDigitalTwin() метод:

await client.UpdateDigitalTwinAsync(twinId, updateTwinData);

Один вызов исправления может обновлять столько свойств в одном двойнике, сколько вы хотите (даже все из них). Если необходимо обновить свойства для нескольких двойников, вам потребуется отдельный вызов обновления для каждого двойника.

Совет

После создания или обновления двойника может возникнуть задержка до 10 секунд, прежде чем изменения будут отражены в запросах. Эта задержка не возникает в API GetDigitalTwin (описанном ранее в этой статье), поэтому, если вы хотите сразу же увидеть обновленные двойники, используйте вызов API вместо запроса.

Ниже приведен пример кода исправления JSON. Этот документ заменяет значения свойств массы и радиуса для цифрового двойника, к который он применяется. В этом примере показана операция JSON Patch replace, которая заменяет значение существующего свойства.

[
    {
      "op": "replace",
      "path": "/mass",
      "value": 0.0799
    },
    {
      "op": "replace",
      "path": "/radius",
      "value": 0.800
    }
  ]

При обновлении двойника из проекта кода с помощью пакета SDK для .NET можно создавать исправления JSON с помощью JsonPatchDocumentпакета SDK для Azure .NET. Ниже приведен пример создания документа исправления JSON и использования UpdateDigitalTwin() в коде проекта.

var updateTwinData = new JsonPatchDocument();
updateTwinData.AppendAdd("/Temperature", 25.0);
updateTwinData.AppendAdd("/myComponent/Property", "Hello");
// Un-set a property
updateTwinData.AppendRemove("/Humidity");

await client.UpdateDigitalTwinAsync("myTwin", updateTwinData).ConfigureAwait(false);

Совет

Вы можете поддерживать метки времени источника в цифровых двойниках, обновив $metadata.<property-name>.sourceTime поле с помощью процесса, описанного в этом разделе. Дополнительные сведения об этом поле и других полях, доступных для записи в цифровых двойниках, см . в формате JSON цифрового двойника.

Обновление вложенных свойств компонентов цифрового двойника

Помните, что модель может содержать компоненты, позволяя ей состоять из других моделей.

Чтобы исправить свойства в компонентах цифрового двойника, можно использовать синтаксис пути в JSON Patch:

[
  {
    "op": "replace",
    "path": "/mycomponentname/mass",
    "value": 0.0799
  }
]

Обновление вложенных свойств в свойствах типа объекта

Модели могут содержать свойства, которые относятся к типу объекта. Эти объекты могут иметь собственные свойства, и может потребоваться обновить одно из этих вложенных свойств, принадлежащих свойству типа объекта. Этот процесс аналогичен процессу обновления вложенных свойств в компонентах, но может потребовать некоторых дополнительных шагов.

Рассмотрим модель со свойством типа объекта ObjectProperty. В ObjectProperty есть строковое свойство с именем StringSubProperty.

При создании двойника с использованием этой модели сразу же создавать экземпляр компонента в ObjectProperty нет необходимости. Если свойство объекта не создается во время создания двойника, путь по умолчанию не создается для доступа ObjectProperty и его StringSubProperty для операции исправления. Прежде чем обновлять его свойства, необходимо добавить путь к ObjectProperty себе.

Это можно сделать с помощью операции обновления JSON add следующим образом:

[
  {
    "op": "add", 
    "path": "/ObjectProperty", 
    "value": {"StringSubProperty":"<string-value>"}
  }
]

Примечание.

Если ObjectProperty содержит более одного свойства, необходимо включить их все в поле value этой операции, даже если обновляется только одно из них:

... "value": {"StringSubProperty":"<string-value>", "Property2":"<property2-value>", ...}

После выполнения этой операции создается путь к StringSubProperty, и объект можно будет обновить непосредственно с помощью обычной операции replace.

[
  {
    "op": "replace",
    "path": "/ObjectProperty/StringSubProperty",
    "value": "<string-value>"
  }
]

Хотя первый шаг не требуется в тех случаях, когда ObjectProperty был создан двойник, рекомендуется использовать его каждый раз при первом обновлении дочернего свойства, так как вы не всегда знаете, было ли изначально создано свойство объекта или нет.

Обновление модели цифрового двойника

Функция UpdateDigitalTwin() может также использоваться для переноса цифрового двойника в другую модель.

Например, рассмотрим следующий код JSON Patch, который заменяет поле $model метаданных цифрового двойника:

[
  {
    "op": "replace",
    "path": "/$metadata/$model",
    "value": "dtmi:example:foo;1"
  }
]

Эта операция выполняется только в том случае, если цифровой двойник, изменяемый исправлением, соответствует новой модели.

Рассмотрим следующий пример:

  1. Представьте цифровой двойник с моделью foo_old. foo_old определяет необходимую массу свойств.
  2. Новая модель foo_new определяет массу свойств и добавляет новую необходимую температуру свойств.
  3. После исправления у цифрового двойника должны быть свойства mass и temperature.

В этой ситуации исправление должно обновить свойство temperature для двойника и модели следующим образом:

[
  {
    "op": "replace",
    "path": "/$metadata/$model",
    "value": "dtmi:example:foo_new;1"
  },
  {
    "op": "add",
    "path": "/temperature",
    "value": 60
  }
]

Обновление исходного времени свойства

При необходимости можно выбрать использование sourceTime поля в свойствах двойника для записи меток времени при наблюдении обновлений свойств в реальном мире. Azure Digital Twins изначально поддерживается sourceTime в метаданных для каждого свойства двойника. Значение sourceTime должно соответствовать формату даты и времени ISO 8601. Дополнительные сведения об этом поле и других полях цифровых двойников см . в формате JSON цифрового двойника.

Минимальная стабильная версия REST API для поддержки этого поля — версия 2022-05-31 . Чтобы работать с этим полем с помощью пакетов SDK для Azure Digital Twins, рекомендуется использовать последнюю версию пакета SDK, чтобы убедиться, что это поле включено.

Ниже приведен пример документа с исправлением JSON, который обновляет значение и sourceTime поле Temperature свойства:

[
  {
    "op": "replace",
    "path": "/Temperature",
    "value": "22.3"
  },
  {
    "op": "replace",
    "path": "/$metadata/Temperature/sourceTime",
    "value": "2021-11-30T18:47:53.7648958Z"
  }
]

Чтобы обновить sourceTime поле в свойстве, которое является частью компонента, включите компонент в начало пути. В приведенном выше примере это можно сделать, изменив значение пути на /$metadata/Temperature/sourceTimemyComponent/$metadata/Temperature/sourceTime.

Примечание.

Если вы обновляете sourceTime и значение свойства, а затем обновляете только значение свойства, sourceTime метка времени первого обновления останется.

Обработка конфликтующих вызовов обновления

Azure Digital Twins обеспечивает последовательную обработку всех входящих запросов. Это означает, что, даже если несколько функций пытаются одновременно обновить одно и то же свойство двойника, нет необходимости писать код явной блокировки для обработки конфликта.

Это поведение действует для каждого двойника.

Представьте ситуацию, в которой приведенные ниже три вызова поступают одновременно:

  • запись свойства А для Twin1;
  • запись свойства В для Twin1;
  • запись свойства А для Twin2.

Два вызова, которые изменяют Twin1, выполняются один за другим, и для каждого изменения создаются соответствующие сообщения. Вызов изменения Twin2 может выполняться одновременно без конфликта, как только он поступает.

Удаление цифрового двойника

Удалить двойники можно с помощью метода DeleteDigitalTwin(). Однако удалить двойник можно только в случае, если у него нет больше связей. Поэтому сначала удалите входящие и исходящие связи двойника.

Ниже приведен пример кода для удаления двойников и их связей. В коде выделен вызов DeleteDigitalTwin пакета SDK, чтобы показать его расположение в более широком контексте.

private static async Task CustomMethod_DeleteTwinAsync(DigitalTwinsClient client, string twinId)
{
    await CustomMethod_FindAndDeleteOutgoingRelationshipsAsync(client, twinId);
    await CustomMethod_FindAndDeleteIncomingRelationshipsAsync(client, twinId);
    try
    {
        await client.DeleteDigitalTwinAsync(twinId);
        Console.WriteLine("Twin deleted successfully");
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"*** Error:{ex.Message}");
    }
}

private static async Task CustomMethod_FindAndDeleteOutgoingRelationshipsAsync(DigitalTwinsClient client, string dtId)
{
    // Find the relationships for the twin

    try
    {
        // GetRelationshipsAsync will throw an error if a problem occurs
        AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);

        await foreach (BasicRelationship rel in rels)
        {
            await client.DeleteRelationshipAsync(dtId, rel.Id).ConfigureAwait(false);
            Console.WriteLine($"Deleted relationship {rel.Id} from {dtId}");
        }
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving or deleting relationships for {dtId} due to {ex.Message}");
    }
}

private static async Task CustomMethod_FindAndDeleteIncomingRelationshipsAsync(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);

        await foreach (IncomingRelationship incomingRel in incomingRels)
        {
            await client.DeleteRelationshipAsync(incomingRel.SourceId, incomingRel.RelationshipId).ConfigureAwait(false);
            Console.WriteLine($"Deleted incoming relationship {incomingRel.RelationshipId} from {dtId}");
        }
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving or deleting incoming relationships for {dtId} due to {ex.Message}");
    }
}

Удаление всех цифровых двойников

Пример удаления всех двойников одновременно можно найти в статье об использовании примера клиентского приложения. Файл CommandLoop.cs выполняет эту операцию в функции CommandDeleteAllTwins().

Примечание.

Если вы хотите удалить все модели, двойники и связи в экземпляре одновременно, используйте API удаления заданий.

Пример готового кода цифрового двойника

Вы можете использовать приведенный ниже пример готового к запуску кода, чтобы создать двойника, обновить сведения о нем и удалить двойника.

Настройка файлов примеров проектов

В этом фрагменте кода используется определение модели Room.json. Чтобы скачать файл модели и использовать его в своем коде, перейдите по этой ссылке непосредственно к файлу в GitHub. Затем щелкните правой кнопкой мыши в любом месте экрана, выберите команду Сохранить как в контекстном меню браузера и в окне "Сохранить как" сохраните файл под названием Room.json.

Затем создайте новый проект консольного приложения в Visual Studio или в любом редакторе.

Затем скопируйте следующий код готового примера в проект:

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

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

            // Create the Azure Digital Twins client for API calls
            string adtInstanceUrl = "https://<your-instance-hostname>";
            var credentials = new DefaultAzureCredential();
            var client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credentials);
            Console.WriteLine($"Service client created – ready to go");

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

            // Create new digital twin
            // <CreateTwin_withHelper>
            string twinId = "myTwinID";
            var initData = new BasicDigitalTwin
            {
                Id = twinId,
                Metadata = { ModelId = "dtmi:example:Room;1" },
                // Initialize properties
                Contents =
                {
                    { "Temperature", 25.0 },
                    { "Humidity", 50.0 },
                },
            };

            // <CreateTwinCall>
            await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twinId, initData);
            // </CreateTwinCall>
            // </CreateTwin_withHelper>
            Console.WriteLine("Twin created successfully");

            //Print twin
            Console.WriteLine("--- Printing twin details:");
            await CustomMethod_FetchAndPrintTwinAsync(twinId, client);
            Console.WriteLine("--------");

            //Update twin data
            var updateTwinData = new JsonPatchDocument();
            updateTwinData.AppendAdd("/Temperature", 30.0);
            // <UpdateTwinCall>
            await client.UpdateDigitalTwinAsync(twinId, updateTwinData);
            // </UpdateTwinCall>
            Console.WriteLine("Twin properties updated");
            Console.WriteLine();

            //Print twin again
            Console.WriteLine("--- Printing twin details (after update):");
            await CustomMethod_FetchAndPrintTwinAsync(twinId, client);
            Console.WriteLine("--------");
            Console.WriteLine();

            //Delete twin
            await CustomMethod_DeleteTwinAsync(client, twinId);
        }

        private static async Task<BasicDigitalTwin> CustomMethod_FetchAndPrintTwinAsync(string twinId, DigitalTwinsClient client)
        {
            // <GetTwin>
            BasicDigitalTwin twin;
            // <GetTwinCall>
            Response<BasicDigitalTwin> twinResponse = await client.GetDigitalTwinAsync<BasicDigitalTwin>(twinId);
            twin = twinResponse.Value;
            // </GetTwinCall>
            Console.WriteLine($"Model id: {twin.Metadata.ModelId}");
            foreach (string prop in twin.Contents.Keys)
            {
                if (twin.Contents.TryGetValue(prop, out object value))
                    Console.WriteLine($"Property '{prop}': {value}");
            }
            // </GetTwin>

            return twin;
        }

        // <DeleteTwin>
        private static async Task CustomMethod_DeleteTwinAsync(DigitalTwinsClient client, string twinId)
        {
            await CustomMethod_FindAndDeleteOutgoingRelationshipsAsync(client, twinId);
            await CustomMethod_FindAndDeleteIncomingRelationshipsAsync(client, twinId);
            try
            {
                await client.DeleteDigitalTwinAsync(twinId);
                Console.WriteLine("Twin deleted successfully");
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"*** Error:{ex.Message}");
            }
        }

        private static async Task CustomMethod_FindAndDeleteOutgoingRelationshipsAsync(DigitalTwinsClient client, string dtId)
        {
            // Find the relationships for the twin

            try
            {
                // GetRelationshipsAsync will throw an error if a problem occurs
                AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);

                await foreach (BasicRelationship rel in rels)
                {
                    await client.DeleteRelationshipAsync(dtId, rel.Id).ConfigureAwait(false);
                    Console.WriteLine($"Deleted relationship {rel.Id} from {dtId}");
                }
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving or deleting relationships for {dtId} due to {ex.Message}");
            }
        }

        private static async Task CustomMethod_FindAndDeleteIncomingRelationshipsAsync(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);

                await foreach (IncomingRelationship incomingRel in incomingRels)
                {
                    await client.DeleteRelationshipAsync(incomingRel.SourceId, incomingRel.RelationshipId).ConfigureAwait(false);
                    Console.WriteLine($"Deleted incoming relationship {incomingRel.RelationshipId} from {dtId}");
                }
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving or deleting incoming relationships for {dtId} due to {ex.Message}");
            }
        }
        // </DeleteTwin>

    }
}

Примечание.

В настоящее время существует известная проблема, влияющая на DefaultAzureCredential класс оболочки, которая может привести к ошибке при проверке подлинности. При возникновении этой проблемы можно попробовать создать экземпляр DefaultAzureCredential с помощью следующего необязательного параметра, чтобы устранить эту проблему: new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true });

Дополнительные сведения об этой проблеме см. в статье об известных проблемах Azure Digital Twins.

Настройка проекта

Затем выполните указанные ниже действия, чтобы настроить код своего проекта.

  1. Добавьте скачанный ранее файл Room.json в проект, а затем замените заполнитель <path-to> в коде, чтобы сообщить программе, где его найти.

  2. Замените заполнитель <your-instance-hostname> именем узла своего экземпляра Azure Digital Twins.

  3. Добавьте две зависимости в проект, необходимые для работы с Azure Digital Twins. Первая — это пакет для пакета SDK Azure Digital Twins для .NET, а вторая содержит средства для проверки подлинности в Azure.

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

Кроме того, необходимо настроить локальные учетные данные, если вы хотите запустить пример напрямую. В следующем разделе показано, как это сделать.

Настройка локальных учетных данных Azure

В этом примере используется DefaultAzureCredential (часть библиотеки Azure.Identity) для аутентификации пользователей с помощью экземпляра Azure Digital Twins, запускаемого на локальном компьютере. Дополнительные сведения о различных способах аутентификации клиентского приложения в Azure Digital Twins см. в статье о записи кода аутентификации приложения.

При использовании DefaultAzureCredential пример будет искать учетные данные в локальной среде, например имя для входа Azure в локальной версии Azure CLI, в Visual Studio либо Visual Studio Code. Поэтому вам нужно войти в Azure локально с помощью одного из этих механизмов, чтобы настроить учетные данные для примера.

Если вы используете Visual Studio или Visual Studio Code для выполнения примеров кода, убедитесь, что вы вошли в этот редактор с теми же учетными данными Azure, которые вы хотите использовать для доступа к экземпляру Azure Digital Twins. Если вы используете локальное окно CLI, выполните az login команду, чтобы войти в учетную запись Azure. После этого при запуске примера кода необходимо автоматически пройти проверку подлинности.

Запуск примера

После завершения установки можно запустить пример проекта кода.

Ниже приведены выходные данные консоли приведенной выше программы:

Screenshot of the console output showing that the twin is created, updated, and deleted.

Следующие шаги

Узнайте, как создавать связи между цифровыми двойниками и управлять ими: