Tutorial: Codificación con el SDK de Azure Digital Twins

Es habitual que los desarrolladores que trabajan con Azure Digital Twins escriban aplicaciones cliente que interactúen con su instancia del servicio Azure Digital Twins. Este tutorial orientado al desarrollador proporciona una introducción a la programación para el servicio Azure Digital Twins, mediante el SDK de Azure Digital Twins para .NET (C#). Le guía a través de la escritura de una aplicación cliente de consola paso a paso en C#, empezando desde cero.

  • Configuración del proyecto
  • Introducción al código del proyecto
  • Ejemplo con todo el código
  • Limpieza de recursos
  • Pasos siguientes

Requisitos previos

En este tutorial de Azure Digital Twins se usa la línea de comandos para configurar el proyecto y trabajar en él. Por definición, puede usar cualquier editor de código para realizar los ejercicios.

Lo que necesita para comenzar:

  • Cualquier editor de código
  • .NET Core 3.1 en la máquina de desarrollo. Esta versión del SDK de .NET Core se puede descargar para varias plataformas desde la página de descarga de .NET Core 3.1.

Preparación de una instancia de Azure Digital Twins

Para poder seguir los pasos que se describen en este artículo y trabajar con Azure Digital Twins, es preciso configurar una instancia de Azure Digital Twins y los permisos necesarios para usarla. Si ya tiene una instancia configurada de Azure Digital Twins, puede usarla y pasar a la sección siguiente. De lo contrario, siga las instrucciones que se indican en Configuración de una instancia y autenticación. Las instrucciones contienen información que le ayudará a comprobar que ha completado cada paso correctamente.

Una vez configurada la instancia, anote el nombre de host de esta. El nombre de host se encuentra en Azure Portal.

Configuración de credenciales locales de Azure

En este ejemplo se usa DefaultAzureCredential (parte de la biblioteca de Azure.Identity) para autenticar a los usuarios mediante la instancia de Azure Digital Twins cuando la ejecuta en la máquina local. Para más información sobre las distintas formas en que una aplicación cliente puede autenticarse con Azure Digital Twins, consulte Escritura de código de autenticación de aplicación.

Con DefaultAzureCredential, el ejemplo buscará las credenciales en el entorno local; por ejemplo, un inicio de sesión de Azure en una DefaultAzureCredential local o en Visual Studio o Visual Studio Code. Por este motivo, debe iniciar sesión en Azure localmente mediante uno de estos mecanismos para configurar las credenciales del ejemplo.

Si usa Visual Studio o Visual Studio Code para ejecutar códigos de ejemplo, asegúrese de que inicia sesión en ese editor con las mismas credenciales de Azure que quiere usar para acceder a la instancia de Azure Digital Twins. Si usa una ventana local de la CLI, ejecute el comando az login para iniciar sesión en la cuenta de Azure. Después de ejecutar el código de ejemplo, debería autenticarse automáticamente.

Configuración del proyecto

Una vez que esté listo para trabajar con la instancia de Azure Digital Twins, empiece a configurar el proyecto de aplicación cliente.

Abra una ventana de la consola en la máquina y cree un directorio de proyecto vacío donde almacenar los elementos que creará al seguir este tutorial. Asigne al directorio el nombre que desee (por ejemplo, DigitalTwinsCodeTutorial).

Vaya al nuevo directorio.

Una vez en el directorio del proyecto, cree un proyecto de aplicación de consola .NET vacío. En la ventana de comandos, puede ejecutar el siguiente comando para crear un proyecto de C# mínimo para la consola:

dotnet new console

Este comando creará varios archivos dentro del directorio, incluido uno llamado Program.cs en el que escribirá la mayor parte del código.

Mantenga abierta la ventana de comandos, ya que la seguirá usando durante todo el tutorial.

A continuación, agregue dos dependencias al proyecto, que necesitará para trabajar con Azure Digital Twins. El primero es el paquete para el SDK de Azure Digital Twins para .NET, el segundo proporciona herramientas para ayudar con la autenticación en Azure.

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

Introducción al código del proyecto

En esta sección, comenzará a escribir el código del nuevo proyecto de aplicación para trabajar con Azure Digital Twins. Las acciones que se van a realizar son:

  • Autenticación en el servicio
  • Carga de un modelo
  • Almacenamiento en caché de los errores
  • Creación de los gemelos digitales
  • Creación de las relaciones
  • Consulta de los gemelos digitales

También hay una sección en la que se muestra el código completo al final del tutorial. Puede usar esta sección como referencia para comprobar el programa a medida que avanza.

Para empezar, abra el archivo Program.cs en cualquier editor de código. Verá una plantilla de código mínimo parecida a esta:

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

En primer lugar, agregue algunas líneas using en la parte superior del código para extraer las dependencias necesarias.

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

Luego, agregará código a este archivo para rellenar alguna funcionalidad.

Autenticación en el servicio

Lo primero que tendrá que hacer la aplicación es autenticarse en el servicio Azure Digital Twins. Después, puede crear una clase de cliente de servicio para acceder a las funciones del SDK.

Para realizar la autenticación, necesita el nombre de host de la instancia de Azure Digital Twins.

En Program.cs, pegue el código siguiente bajo la línea "Hello, World!" print en el método Main. Establezca el valor de adtInstanceUrl en el nombre de host de la instancia de Azure Digital Twins.

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");

Guarde el archivo.

En la ventana de comandos, ejecute el código con este comando:

dotnet run

Este comando restaurará las dependencias en la primera ejecución y, luego, ejecutará el programa.

  • Si no se produce ningún error, el programa devolverá el mensaje "Se ha creado el servicio de cliente: listo para continuar".
  • Cómo aún no hay ningún control de errores en este proyecto, si hay algún problema, verá que el código inicia una excepción.

Nota:

Actualmente hay un problema conocido que afecta a la clase contenedora DefaultAzureCredential que puede dar lugar a un error durante la autenticación. Si se produce este problema, puede intentar crear instancias de DefaultAzureCredential con el siguiente parámetro opcional para resolverlo: new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true });

Para más información sobre este problema, consulte Problemas conocidos de Azure Digital Twins.

Carga de un modelo

Azure Digital Twins carece de vocabulario de dominio intrínseco. Los tipos de elementos del entorno que puede representar en Azure Digital Twins los define el usuario mediante modelos. Los modelos son parecidos a las clases de los lenguajes de programación orientados a objetos en el sentido de que proporcionan plantillas definidas por el usuario para gemelos digitales, las cuales se siguen. Más adelante también se crean instancias de estas plantillas. Se escriben en un lenguaje similar a JSON denominado Lenguaje de definición de Digital Twins (DTDL) .

El primer paso para crear una solución de Azure Digital Twins es definir al menos un modelo en un archivo DTDL.

En el directorio en el que creó el proyecto, cree un archivo .json llamado SampleModel.json. Pegue el siguiente cuerpo de archivo:

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

Sugerencia

Si va a usar Visual Studio en este tutorial, es posible que quiera seleccionar el archivo JSON recién creado y establecer la propiedad Copiar en el directorio de salida del inspector de propiedad en Copiar si es más reciente o Copia siempre. De esta forma, Visual Studio podrá encontrar el archivo JSON con la ruta de acceso predeterminada cuando ejecute el programa con F5 durante el resto del tutorial.

Sugerencia

Puede comprobar los documentos del modelo para asegurarse de que el DTDL es válido mediante la Biblioteca DTDLParser. Para más información sobre el uso de esta biblioteca, compruebe Análisis y validación de modelos.

Después, agregue más código a Program.cs para cargar el modelo que ha creado en la instancia de Azure Digital Twins.

Primero, agregue algunas instrucciones using en la parte superior del archivo:

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

A continuación, prepárese para usar los métodos asincrónicos del SDK de servicio de C#; para ello, cambie la firma del método Main y así permitir la ejecución asincrónica.

static async Task Main(string[] args)
{

Nota

El uso de async no es estrictamente necesario, ya que el SDK también proporciona versiones sincrónicas de todas las llamadas. En este tutorial se practica con async.

A continuación, viene el primer bit de código que interactúa con el servicio Azure Digital Twins. Este código carga el archivo DTDL que creó desde el disco y, luego, lo carga en la instancia del servicio Azure Digital Twins.

Pegue el código siguiente en el código de autorización que agregó 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);

En la ventana de comandos, ejecute el programa con este comando:

dotnet run

En la salida se imprimirá "Upload a model" (Cargar un modelo), lo que indica que se ha llegado al código, pero aún no hay ninguna salida para indicar si la carga se realizó correctamente o no.

Para agregar una instrucción de impresión que muestre que todos los modelos se han cargado correctamente en la instancia, agregue el código siguiente justo después de la sección 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 volver a ejecutar el programa para probar este nuevo código, recuerde que la última vez que ejecutó el programa ya se cargó el modelo. Azure Digital Twins no permite cargar un mismo modelo dos veces, por lo que si intenta volver a cargar un modelo, el programa debería generar una excepción.

Teniendo esto en cuenta, ejecute de nuevo el programa con este comando en la ventana de comandos:

dotnet run

El programa iniciará una excepción. Al intentar cargar un modelo que ya se ha cargado, el servicio devuelve un error de "solicitud incorrecta" a través de la API REST. Como resultado, el SDK de cliente de Azure Digital Twins inicia a su vez una excepción por cada servicio que devuelva un código que no sea correcto.

La siguiente sección trata de excepciones como esta y como abordarlas en el código.

Almacenamiento en caché de los errores

Para evitar que el programa se bloquee, puede agregar código de excepción alrededor del código de carga del modelo. Encapsule la llamada al cliente existente await client.CreateModelsAsync(typeList) en un controlador try/catch, como este:

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

Ejecute de nuevo el programa con dotnet run en la ventana de comandos. Verá que recibe más detalles sobre el problema de carga del modelo, incluido un código de error que indica que ModelIdAlreadyExists.

A partir de este punto, en el tutorial se encapsulan todas las llamadas a los métodos de servicio de los controladores try/catch.

Creación de gemelos digitales

Ahora que ha cargado un modelo en Azure Digital Twins, puede usar esta definición de modelo para crear gemelos digitales. Los gemelos digitales son instancias de un modelo y representan las entidades del entorno empresarial, como los sensores de una granja, las salas de un edificio o las luces de un coche. En esta sección se crean algunos gemelos digitales basados en el modelo que cargó anteriormente.

Agregue el código siguiente al final del método Main para crear e inicializar tres gemelos digitales basados en este 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}");
    }
}

En la ventana de comandos, ejecute el programa con dotnet run. En la salida, busque los mensajes de impresión de que se han creado sampleTwin-0, sampleTwin-1y sampleTwin-2.

Luego, Vuelva a ejecutar el programa.

Tenga en cuenta que no se produce ningún error cuando los gemelos se crean la segunda vez, aunque ya existen después de la primera ejecución. A diferencia de la creación de modelos, la creación de gemelos es, en el nivel de REST, una llamada PUT con semántica upsert. El uso de este tipo de llamada REST significa que si ya existe un gemelo, un intento de volver a crear el mismo gemelo reemplazará al gemelo original. No aparece ningún error.

Crear relaciones

A continuación, puede crear relaciones entre estos gemelos que ha creado para conectarlos en un gráfico de gemelos. Los gráficos de gemelos se usan para representar un entorno completo.

Agregue un nuevo método estático a la clase Program, debajo del método Main: (ahora el código tiene dos 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}");
    }
}

A continuación, agregue el siguiente código al final del método Main para llamar al método CreateRelationship y utilizar el código que acaba de escribir:

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

En la ventana de comandos, ejecute el programa con dotnet run. En la salida, busque instrucciones de impresión que digan que las dos relaciones se han creado correctamente.

Azure Digital Twins no permite crear una relación si ya existe otra con el mismo identificador, por lo que, si ejecuta el programa varias veces, observará excepciones en la creación de la relación. Este código almacena en caché las excepciones y las omite.

Enumeración de las relaciones

El siguiente código que va a agregar le permite ver la lista de relaciones que ha creado.

Agregue el siguiente método nuevo a la clase 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}");
    }
}

Luego, agregue el siguiente código al final del método Main para llamar al código ListRelationships:

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

En la ventana de comandos, ejecute el programa con dotnet run. Debería ver una relación de todas las relaciones que ha creado en una instrucción de salida similares a esta:

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

Consulta de los gemelos digitales

Una de las principales características de Azure Digital Twins es la posibilidad de consultar el gráfico de gemelos de forma fácil y eficaz para responder a las preguntas sobre el entorno.

La última sección del código que se va a agregar en este tutorial ejecuta una consulta en la instancia de Azure Digital Twins. La consulta usada en este ejemplo devuelve todos los gemelos digitales de la instancia.

Agregue esta instrucción using para permitir el uso de la clase JsonSerializer para ayudar a presentar la información de los gemelos digitales:

using System.Text.Json;

Luego, agregue el siguiente código al final del 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("---------------");
}

En la ventana de comandos, ejecute el programa con dotnet run. En la salida verá todos los gemelos digitales de esta instancia.

Nota

Después de realizar un cambio en los datos del gráfico, puede haber una latencia de hasta 10 segundos antes de que los cambios se reflejen en las consultas.

La API de DigitalTwins refleja los cambios inmediatamente, por lo que si necesita una respuesta instantánea, use una solicitud de API (DigitalTwins GetById) o una llamada SDK (GetDigitalTwin) para obtener datos gemelos en lugar de una consulta.

Finalización del ejemplo de código

En este punto del tutorial, tiene una aplicación cliente completa, que puede llevar a cabo acciones básicas en Azure Digital Twins. Como referencia, a continuación, se muestra el código completo del programa en Program.cs:

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>
    }
}

Limpieza de recursos

Después de completar este tutorial, puede elegir los recursos que quiere quitar en función de lo que quiera hacer a continuación.

  • Si tiene pensado continuar con el siguiente tutorial, la instancia usada en este tutorial se puede reutilizar en el siguiente. Puede conservar los recursos de Azure Digital Twins que configure aquí y omitir el resto de esta sección.
  • Si quiere seguir usando la instancia de Azure Digital Twins de este artículo, pero borrar todos sus modelos, gemelos y relaciones, ejecute el siguiente comando de CLI az dt job deletion:

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

    Si solo desea eliminar algunos de estos elementos, puede usar los comandos az dt twin relationship delete, az dt twin delete y az dt model delete para eliminar de forma selectiva solo los elementos que desea quitar.

  • Si no necesita ninguno de los recursos que creó en este tutorial, puede eliminar la instancia de Azure Digital Twins y todos los demás recursos de este artículo con el comando de la CLI az group delete. Esto permite eliminar todos los recursos de Azure de un grupo de recursos, así como el grupo en sí.

    Importante

    La eliminación de un grupo de recursos es irreversible. El grupo de recursos y todos los recursos contenidos en él se eliminan permanentemente. Asegúrese de no eliminar por accidente el grupo de recursos o los recursos equivocados.

    Abra Azure Cloud Shell o una ventana de la CLI local y ejecute el comando siguiente para eliminar el grupo de recursos y todo lo que contiene.

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

También puede que desee eliminar la carpeta del proyecto de la máquina local.

Pasos siguientes

En este tutorial, ha creado una aplicación cliente de consola .NET desde cero. Escribió código para ella de forma que realizara las acciones básicas en una instancia de Azure Digital Twins.

Continúe con el siguiente tutorial para explorar lo que puede hacer con esta aplicación cliente de ejemplo: