Tutorial: Codificação com o SDK dos Gêmeos Digitais do Azure

Os desenvolvedores que trabalham com os Gêmeos Digitais do Azure escrevem aplicativos clientes para interagir com a instância do serviço dos Gêmeos Digitais do Azure. Este tutorial voltado para o desenvolvedor fornece uma introdução à programação no serviço Gêmeos Digitais do Azure usando o SDK dos Gêmeos Digitais do Azure para .NET (C#). Ele mostra como escrever um aplicativo cliente de console C# passo a passo do zero.

  • Configurar o projeto
  • Introdução ao código do projeto
  • Exemplo de código completo
  • Limpar os recursos
  • Próximas etapas

Pré-requisitos

Este tutorial dos Gêmeos Digitais do Azure usa a linha de comando para instalação e trabalho de projeto. Assim, você pode usar qualquer editor de códigos para acompanhar os exercícios.

O que você precisa para começar:

  • Qualquer editor de códigos
  • .NET Core 3.1 no computador de desenvolvimento. Baixe esta versão do SDK do .NET Core para várias plataformas em Baixar o .NET Core 3.1.

Preparar uma instância dos Gêmeos Digitais do Azure

Para trabalhar com os Gêmeos Digitais do Azure neste artigo, você precisará ter uma instância dos Gêmeos Digitais do Azure e as permissões necessárias para usá-la. Se você já tiver uma instância dos Gêmeos Digitais do Azure configurada, use essa instância e vá direto para a próxima seção. Caso contrário, siga as instruções descritas em Configurar uma instância e uma autenticação. As instruções contêm informações que ajudarão você a verificar se cada etapa foi concluída com êxito.

Após configurar a instância, anote o nome do host da instância. Encontre o nome do host no portal do Azure.

Configurar credenciais locais do Azure

Quando você o executa em seu computador local, este exemplo usa o DefaultAzureCredential (parte da biblioteca Azure.Identity) para autenticar usuários com uma instância dos Gêmeos Digitais do Azure. Para obter mais informações sobre as diferentes maneiras como um aplicativo cliente pode se autenticar com os Gêmeos Digitais do Azure, confira Escrever o código de autenticação do aplicativo.

Com DefaultAzureCredential, o exemplo pesquisará as credenciais no ambiente local, como uma conexão do Azure em uma DefaultAzureCredential local ou no Visual Studio ou Visual Studio Code. Por isso, será necessário entrar no Azure localmente por meio de um desses mecanismos para configurar as credenciais do exemplo.

Caso esteja usando o Visual Studio ou o Visual Studio Code para executar exemplos de código, verifique se está conectado a esse editor com as mesmas credenciais do Azure que deseja usar para acessar a instância dos Gêmeos Digitais do Azure. Se estiver usando uma janela da CLI local, execute o comando az login para entrar na sua conta do Azure. Depois disso, quando você executar o exemplo de código, será autenticado automaticamente.

Configurar o projeto

Quando tudo estiver pronto para começar a usar sua instância dos Gêmeos Digitais do Azure, inicie a configuração do projeto de aplicativo cliente.

Abra uma janela do console no computador e crie um diretório de projeto vazio no qual deseja armazenar seu trabalho durante este tutorial. Nomeie o diretório como quiser (por exemplo, DigitalTwinsCodeTutorial).

Acesse o novo diretório.

Quando estiver no diretório do projeto, crie um projeto de aplicativo de console .NET vazio. Na janela de comando, é possível executar o seguinte comando a fim de criar um projeto C# mínimo para o console:

dotnet new console

Esse comando criará vários arquivos dentro do diretório, incluindo um chamado Program.cs, no qual você escreverá a maior parte do código.

Mantenha a janela Comando aberta, pois você continuará usando-a em todo o tutorial.

Depois, adicione duas dependências ao seu projeto, que serão necessárias para trabalhar com os Gêmeos Digitais do Azure. A primeira é o pacote do SDK dos Gêmeos Digitais do Azure para .NET, e a segunda fornece ferramentas para ajudar com a autenticação no Azure.

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

Introdução ao código do projeto

Nesta seção, você começará a escrever o código do novo projeto de aplicativo para trabalhar com os Gêmeos Digitais do Azure. As ações abordadas incluem:

  • Autenticação no serviço
  • Upload de um modelo
  • Captura de erros
  • Criação de gêmeos digitais
  • Criação de relações
  • Consulta de gêmeos digitais

Também há uma seção que mostra o código completo no final do tutorial. Use essa seção como uma referência para verificar seu programa durante as etapas.

Para começar, abra o arquivo Program.cs em qualquer editor de códigos. Você verá um modelo com o mínimo de código que terá a seguinte aparência:

Screenshot of a snippet of sample code in a code editor.

Primeiro, adicione algumas linhas using à parte superior do código para extrair as dependências necessárias.

using Azure.DigitalTwins.Core;
using Azure.Identity;

Em seguida, você adicionará o código a esse arquivo para preencher algumas funcionalidades.

Autenticar-se no serviço

A primeira coisa que o seu aplicativo precisará fazer é se autenticar no serviço Gêmeos Digitais do Azure. Em seguida, você poderá criar uma classe de cliente de serviço para acessar as funções do SDK.

Para a autenticação, você precisa do nome do host da instância dos Gêmeos Digitais do Azure.

Em Program.cs, cole o código a seguir abaixo da linha de impressão "Olá, Mundo!" no método Main. Defina o valor de adtInstanceUrl como o nome do host da instância dos Gêmeos Digitais do Azure.

string adtInstanceUrl = "https://<your-Azure-Digital-Twins-instance-hostName>"; 

var credential = new DefaultAzureCredential();
var client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credential);
Console.WriteLine($"Service client created – ready to go");

Salve o arquivo.

Na janela Comando, execute o código com este comando:

dotnet run

Esse comando vai restaurar as dependências na primeira execução e executar o programa.

  • Se nenhum erro ocorrer, o programa mostrará a mensagem: "Cliente de serviço criado – pronto para uso".
  • Como ainda não há tratamento de erro neste projeto, se houver problemas, você verá uma exceção gerada pelo código.

Observação

Atualmente, há um problema conhecido que afeta a classe wrapper DefaultAzureCredential que pode resultar em erro durante a autenticação. Se encontrar esse problema, você pode tentar criar uma instância DefaultAzureCredential com o seguinte parâmetro opcional para resolver: new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true });

Para obter mais informações sobre esse problema, consulte Problemas conhecidos dos Gêmeos Digitais do Azure.

Carregar um modelo

Os Gêmeos Digitais do Azure não têm um vocabulário de domínio intrínseco. Os tipos de elementos no seu ambiente que podem ser representados nos Gêmeos Digitais do Azure são definidos por você por meio de modelos. Os modelos são semelhantes às classes nas linguagens de programação orientadas a objeto; elas fornecem modelos definidos pelo usuário para os gêmeos digitais seguirem e criarem uma instância deles posteriormente. Eles são escritos em uma linguagem semelhante a JSON chamada DTDL (Digital Twins Definition Language) .

A primeira etapa na criação de uma solução dos Gêmeos Digitais do Azure é definir, pelo menos, um modelo em um arquivo DTDL.

No diretório em que você criou o projeto, crie um arquivo .json chamado SampleModel.json. Cole-o no corpo do seguinte arquivo:

{
  "@id": "dtmi:example:SampleModel;1",
  "@type": "Interface",
  "displayName": "SampleModel",
  "contents": [
    {
      "@type": "Relationship",
      "name": "contains"
    },
    {
      "@type": "Property",
      "name": "data",
      "schema": "string"
    }
  ],
  "@context": "dtmi:dtdl:context;3"
}

Dica

Se você estiver usando o Visual Studio para este tutorial, o ideal será selecionar o arquivo JSON recém-criado e definir a propriedade Copiar para o Diretório de Saída na inspeção de propriedade como Copiar se for o Mais Recente ou Sempre Copiar. Isso permitirá que o Visual Studio localize o arquivo JSON com o caminho padrão quando você executar o programa com F5 durante o restante do tutorial.

Dica

Verifique os documentos de modelo para garantir que o DTDL seja válido usando a biblioteca DTDLParser. Para obter mais informações sobre como usar essa biblioteca, confira Analisar e validar modelos.

Em seguida, adicione mais um código a Program.cs para carregar o modelo criado na sua instância dos Gêmeos Digitais do Azure.

Primeiro, adicione algumas instruções using à parte superior do arquivo:

using System.Threading.Tasks;
using System.IO;
using System.Collections.Generic;
using Azure;

Em seguida, prepare-se para usar os métodos assíncronos no SDK do serviço C#, alterando a assinatura do método Main para permitir a execução assíncrona.

static async Task Main(string[] args)
{

Observação

O uso de async não é estritamente necessário, pois o SDK também fornece versões síncronas de todas as chamadas. Este tutorial ensina o uso de async.

Em seguida, vem o primeiro trecho de código que interage com o serviço Gêmeos Digitais do Azure. Esse código carrega o arquivo DTDL que você criou no disco e carrega-o na instância do serviço Gêmeos Digitais do Azure.

Cole o código a seguir no código de autorização adicionado anteriormente.

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

Na janela Comando, execute o programa com este comando:

dotnet run

"Carregar um modelo" será impresso na saída, indicando que esse código foi atingido, mas ainda não há nenhuma saída para indicar se o upload foi bem-sucedido.

Para adicionar uma instrução de impressão mostrando todos os modelos que foram carregados com êxito na instância, adicione o seguinte código logo após a seção anterior:

// Read a list of models back from the service
AsyncPageable<DigitalTwinsModelData> modelDataList = client.GetModelsAsync();
await foreach (DigitalTwinsModelData md in modelDataList)
{
    Console.WriteLine($"Model: {md.Id}");
}

Antes de executar o programa novamente para testar esse novo código, lembre-se de que a última vez que você executou o programa, você já tinha carregado o modelo. Os Gêmeos Digitais do Azure não permitem que você carregue o mesmo modelo duas vezes. Sendo assim, se você tentar carregar o mesmo modelo novamente, o programa deverá lançar uma exceção.

Com essa informação em mente, execute o programa novamente com este comando na janela comando:

dotnet run

O programa deverá gerar uma exceção. Quando você tenta carregar um modelo que já foi carregado, o serviço retorna um erro de "solicitação inválida" por meio da API REST. Como resultado, o SDK do cliente dos Gêmeos Digitais do Azure, por sua vez, vai gerar uma exceção para cada código de retorno do serviço diferente de êxito.

A próxima seção aborda exceções como essa e como tratá-las no código.

Capturar erros

Para impedir a falha do programa, adicione um código de exceção ao código de upload do modelo. Encapsule a chamada de cliente existente await client.CreateModelsAsync(typeList) em um manipulador try/catch desta forma:

try
{
    await client.CreateModelsAsync(models);
    Console.WriteLine("Models uploaded to the instance:");
}
catch (RequestFailedException e)
{
    Console.WriteLine($"Upload model error: {e.Status}: {e.Message}");
}

Execute o programa novamente com dotnet run na janela de comando. Você verá que obterá mais detalhes sobre o problema de upload do modelo, incluindo um código de erro informando que ModelIdAlreadyExists.

Daqui em diante, o tutorial encapsulará todas as chamadas a métodos de serviço em manipuladores try/catch.

Criar gêmeos digitais

Agora que você carregou um modelo nos Gêmeos Digitais do Azure, use essa definição de modelo para criar gêmeos digitais. Os gêmeos digitais são instâncias de um modelo e representam as entidades no seu ambiente de negócios – itens como sensores em um farm, salas em um prédio ou luzes em um carro. Esta seção cria alguns gêmeos digitais com base no modelo carregado anteriormente.

Adicione o código a seguir ao final do método Main para criar e inicializar três gêmeos digitais com base nesse modelo.

var twinData = new BasicDigitalTwin();
twinData.Metadata.ModelId = "dtmi:example:SampleModel;1";
twinData.Contents.Add("data", $"Hello World!");

string prefix = "sampleTwin-";
for (int i = 0; i < 3; i++)
{
    try
    {
        twinData.Id = $"{prefix}{i}";
        await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twinData.Id, twinData);
        Console.WriteLine($"Created twin: {twinData.Id}");
    }
    catch(RequestFailedException e)
    {
        Console.WriteLine($"Create twin error: {e.Status}: {e.Message}");
    }
}

Na janela Comando, execute o programa com dotnet run. Na saída, procure as mensagens de impressão que indicam que sampleTwin-0, sampleTwin-1 e sampleTwin-2 foram criados.

Depois, execute o programa novamente.

Observe que nenhum erro é gerado quando os gêmeos são criados na segunda vez, mesmo que eles já existam após a primeira execução. Ao contrário da criação do modelo, a criação de gêmeos é, no nível da REST, uma chamada PUT com a semântica de upsert. O uso dessa chamada REST significa que, se já houver um gêmeo, uma tentativa de criar o mesmo gêmeo novamente apenas substituirá o gêmeo original. Nenhum erro será gerado.

Criar relações

Em seguida, você pode criar relações entre os gêmeos criados para conectá-los a um grafo de gêmeos. Os grafos de gêmeos são usados para representar todo o ambiente.

Adicione um novo método estático à classe Program, abaixo do método Main (o código agora tem dois métodos):

public async static Task CreateRelationshipAsync(DigitalTwinsClient client, string srcId, string targetId)
{
    var relationship = new BasicRelationship
    {
        TargetId = targetId,
        Name = "contains"
    };

    try
    {
        string relId = $"{srcId}-contains->{targetId}";
        await client.CreateOrReplaceRelationshipAsync(srcId, relId, relationship);
        Console.WriteLine("Created relationship successfully");
    }
    catch (RequestFailedException e)
    {
        Console.WriteLine($"Create relationship error: {e.Status}: {e.Message}");
    }
}

Em seguida, adicione o seguinte código ao final do método Main para chamar o método CreateRelationship e usar o código que você acabou de escrever:

// Connect the twins with relationships
await CreateRelationshipAsync(client, "sampleTwin-0", "sampleTwin-1");
await CreateRelationshipAsync(client, "sampleTwin-0", "sampleTwin-2");

Na janela Comando, execute o programa com dotnet run. Na saída, procure instruções de impressão indicando que as duas relações foram criadas com êxito.

Os Gêmeos Digitais do Azure não permitirão que você crie uma relação se já existir outra com a mesma ID. Portanto, se você executar o programa várias vezes, receberá exceções na criação de relação. Este código captura as exceções e as ignora.

Listar relações

O próximo código que você adicionará permite ver a lista de relações criadas.

Adicione o seguinte método à classe Program:

public async static Task ListRelationshipsAsync(DigitalTwinsClient client, string srcId)
{
    try
    {
        AsyncPageable<BasicRelationship> results = client.GetRelationshipsAsync<BasicRelationship>(srcId);
        Console.WriteLine($"Twin {srcId} is connected to:");
        await foreach (BasicRelationship rel in results)
        {
            Console.WriteLine($" -{rel.Name}->{rel.TargetId}");
        }
    }
    catch (RequestFailedException e)
    {
        Console.WriteLine($"Relationship retrieval error: {e.Status}: {e.Message}");
    }
}

Em seguida, adicione o seguinte código ao final do método Main para chamar o código ListRelationships:

//List the relationships
await ListRelationshipsAsync(client, "sampleTwin-0");

Na janela Comando, execute o programa com dotnet run. Você verá uma lista de todas as relações criadas em uma instrução de saída parecida com esta:

Screenshot of a console showing the program output, which results in a message that lists the twin relationships.

Consultar os gêmeos digitais

Um dos principais recursos dos Gêmeos Digitais do Azure é a capacidade de consultar o grafo de gêmeos com facilidade e eficiência para responder às perguntas sobre o seu ambiente.

A última seção de código a ser adicionada neste tutorial executa uma consulta na instância dos Gêmeos Digitais do Azure. A consulta usada neste exemplo retorna todos os gêmeos digitais na instância.

Adicione esta instrução using para permitir o uso da classe JsonSerializer a fim de ajudar a apresentar as informações do gêmeo digital:

using System.Text.Json;

Depois, adicione o seguinte código ao final do método Main:

// Run a query for all twins
string query = "SELECT * FROM digitaltwins";
AsyncPageable<BasicDigitalTwin> queryResult = client.QueryAsync<BasicDigitalTwin>(query);

await foreach (BasicDigitalTwin twin in queryResult)
{
    Console.WriteLine(JsonSerializer.Serialize(twin));
    Console.WriteLine("---------------");
}

Na janela Comando, execute o programa com dotnet run. Você deverá ver todas os gêmeos digitais nessa instância na saída.

Observação

Após a realização de uma alteração nos dados do grafo, talvez haja uma latência de até dez segundos antes que as alterações sejam exibidas nas consultas.

A API do DigitalTwins reflete as alterações imediatamente. Portanto, se você precisar de uma resposta instantânea, use uma solicitação de API (DigitalTwins GetById) ou uma chamada de SDK (GetDigitalTwin) para obter dados dos gêmeos em vez de uma consulta.

Exemplo de código completo

Neste ponto do tutorial, você tem um aplicativo cliente completo, capaz de executar ações básicas nos Gêmeos Digitais do Azure. Para referência, o código completo do programa em Program.cs é listado abaixo:

using System;
// <Azure_Digital_Twins_dependencies>
using Azure.DigitalTwins.Core;
using Azure.Identity;
// </Azure_Digital_Twins_dependencies>
// <Model_dependencies>
using System.Threading.Tasks;
using System.IO;
using System.Collections.Generic;
using Azure;
// </Model_dependencies>
// <Query_dependencies>
using System.Text.Json;
// </Query_dependencies>

namespace DigitalTwins_Samples
{
    class DigitalTwinsClientAppSample
    {
        // <Async_signature>
        static async Task Main(string[] args)
        {
        // </Async_signature>
            Console.WriteLine("Hello World!");
            // <Authentication_code>
            string adtInstanceUrl = "https://<your-Azure-Digital-Twins-instance-hostName>"; 
            
            var credential = new DefaultAzureCredential();
            var client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credential);
            Console.WriteLine($"Service client created – ready to go");
            // </Authentication_code>

            // <Model_code>
            Console.WriteLine();
            Console.WriteLine("Upload a model");
            string dtdl = File.ReadAllText("SampleModel.json");
            var models = new List<string> { dtdl };

            // Upload the model to the service
            // <Model_try_catch>
            try
            {
                await client.CreateModelsAsync(models);
                Console.WriteLine("Models uploaded to the instance:");
            }
            catch (RequestFailedException e)
            {
                Console.WriteLine($"Upload model error: {e.Status}: {e.Message}");
            }
            // </Model_try_catch>

            // <Print_model>
            // Read a list of models back from the service
            AsyncPageable<DigitalTwinsModelData> modelDataList = client.GetModelsAsync();
            await foreach (DigitalTwinsModelData md in modelDataList)
            {
                Console.WriteLine($"Model: {md.Id}");
            }
            // </Print_model>
            // </Model_code>

            // <Initialize_twins>
            var twinData = new BasicDigitalTwin();
            twinData.Metadata.ModelId = "dtmi:example:SampleModel;1";
            twinData.Contents.Add("data", $"Hello World!");
            
            string prefix = "sampleTwin-";
            for (int i = 0; i < 3; i++)
            {
                try
                {
                    twinData.Id = $"{prefix}{i}";
                    await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twinData.Id, twinData);
                    Console.WriteLine($"Created twin: {twinData.Id}");
                }
                catch(RequestFailedException e)
                {
                    Console.WriteLine($"Create twin error: {e.Status}: {e.Message}");
                }
            }
            // </Initialize_twins>

            // <Use_create_relationship>
            // Connect the twins with relationships
            await CreateRelationshipAsync(client, "sampleTwin-0", "sampleTwin-1");
            await CreateRelationshipAsync(client, "sampleTwin-0", "sampleTwin-2");
            // </Use_create_relationship>

            // <Use_list_relationships>
            //List the relationships
            await ListRelationshipsAsync(client, "sampleTwin-0");
            // </Use_list_relationships>

            // <Query_twins>
            // Run a query for all twins
            string query = "SELECT * FROM digitaltwins";
            AsyncPageable<BasicDigitalTwin> queryResult = client.QueryAsync<BasicDigitalTwin>(query);
            
            await foreach (BasicDigitalTwin twin in queryResult)
            {
                Console.WriteLine(JsonSerializer.Serialize(twin));
                Console.WriteLine("---------------");
            }
            // </Query_twins>
        }

        // <Create_relationship>
        public async static Task CreateRelationshipAsync(DigitalTwinsClient client, string srcId, string targetId)
        {
            var relationship = new BasicRelationship
            {
                TargetId = targetId,
                Name = "contains"
            };
        
            try
            {
                string relId = $"{srcId}-contains->{targetId}";
                await client.CreateOrReplaceRelationshipAsync(srcId, relId, relationship);
                Console.WriteLine("Created relationship successfully");
            }
            catch (RequestFailedException e)
            {
                Console.WriteLine($"Create relationship error: {e.Status}: {e.Message}");
            }
        }
        // </Create_relationship>
        
        // <List_relationships>
        public async static Task ListRelationshipsAsync(DigitalTwinsClient client, string srcId)
        {
            try
            {
                AsyncPageable<BasicRelationship> results = client.GetRelationshipsAsync<BasicRelationship>(srcId);
                Console.WriteLine($"Twin {srcId} is connected to:");
                await foreach (BasicRelationship rel in results)
                {
                    Console.WriteLine($" -{rel.Name}->{rel.TargetId}");
                }
            }
            catch (RequestFailedException e)
            {
                Console.WriteLine($"Relationship retrieval error: {e.Status}: {e.Message}");
            }
        }
        // </List_relationships>
    }
}

Limpar os recursos

Depois de concluir este tutorial, você poderá escolher quais recursos gostaria de remover, dependendo do que você gostaria de fazer em seguida.

  • Se você planeja prosseguir para o próximo tutorial, a instância usada neste tutorial pode ser reutilizada no próximo. Você pode manter os recursos dos Gêmeos Digitais do Azure configurados aqui e ignorar o restante desta seção.
  • Se você quiser continuar usando a instância dos Gêmeos Digitais do Azure deste artigo, mas limpar todos os seus modelos, gêmeos e relações, execute o seguinte comando da CLI az dt job deletion:

    az dt job deletion create -n <name-of-Azure-Digital-Twins-instance> -y
    

    Se você quiser apenas excluir alguns desses elementos, poderá usar os comandos az dt twin relationship delete, az dt twin delete e az dt model delete para excluir seletivamente apenas os elementos que você deseja remover.

  • Caso não precise de nenhum dos recursos criados neste tutorial, você poderá excluir a instância dos Gêmeos Digitais do Azure e todos os outros recursos deste artigo com o comando az group delete da CLI. Isso exclui todos os recursos do Azure em um grupo de recursos, bem como o grupo de recursos em si.

    Importante

    A exclusão de um grupo de recursos é irreversível. O grupo de recursos e todos os recursos contidos nele são excluídos permanentemente. Não exclua acidentalmente grupo de recursos ou recursos incorretos.

    Abra o Azure Cloud Shell ou uma janela da CLI local e execute o comando a seguir para excluir o grupo de recursos e tudo o que ele contém.

    az group delete --name <your-resource-group>
    

Talvez seja interessante excluir a pasta do projeto do computador local.

Próximas etapas

Neste tutorial, você criou um aplicativo cliente de console .NET do zero. Você escreveu o código para esse aplicativo cliente a fim de executar as ações básicas em uma instância dos Gêmeos Digitais do Azure.

Prossiga para o próximo tutorial e explore as coisas que você pode fazer com um aplicativo cliente de exemplo: