How to update a digital twin object property using Azure Service Bus queue data from an Iot Central device

GuidoL 310 Reputation points
2023-05-23T15:55:45.18+00:00

Hi,

i'm trying to write C# code to update a digital twin object property using Azure Service Bus queue data from an Iot Central device that sends telemetry.

Azure Service Bus queue works fine and correctly receives telemetry data from an Iot Central device.

Here is the device template:

{
 "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest;1",
 "@type": "Interface",
 "contents": [
  {
   "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest:LibraryItemData;1",
   "@type": "Telemetry",
   "displayName": {
    "en": "LibraryItemData"
   },
   "name": "LibraryItemData",
   "schema": {
    "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest:LibraryItemData:schema;1",
    "@type": "Object",
    "displayName": {
     "en": "Object"
    },
    "fields": [
     {
      "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest:LibraryItemData:schema:ItemId;1",
      "displayName": {
       "en": "ItemId"
      },
      "name": "ItemId",
      "schema": "string"
     },
     {
      "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest:LibraryItemData:schema:Description;1",
      "displayName": {
       "en": "Description"
      },
      "name": "Description",
      "schema": "string"
     },
     {
      "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest:LibraryItemData:schema:Location;1",
      "displayName": {
       "en": "Location"
      },
      "name": "Location",
      "schema": "string"
     },
     {
      "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest:LibraryItemData:schema:DateTimeValue;1",
      "displayName": {
       "en": "DateTimeValue"
      },
      "name": "DateTimeValue",
      "schema": "dateTime"
     }
    ]
   }
  },
  {
   "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest:LibraryItemDataProperty;1",
   "@type": "Property",
   "displayName": {
    "en": "LibraryItemDataProperty"
   },
   "name": "LibraryItemDataProperty",
   "schema": {
    "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest:LibraryItemDataProperty:schema;1",
    "@type": "Object",
    "displayName": {
     "en": "Object"
    },
    "fields": [
     {
      "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest:LibraryItemDataProperty:schema:ItemId;1",
      "displayName": {
       "en": "ItemId"
      },
      "name": "ItemId",
      "schema": "string"
     },
     {
      "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest:LibraryItemDataProperty:schema:Description;1",
      "displayName": {
       "en": "Description"
      },
      "name": "Description",
      "schema": "string"
     },
     {
      "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest:LibraryItemDataProperty:schema:Location;1",
      "displayName": {
       "en": "Location"
      },
      "name": "Location",
      "schema": "string"
     },
     {
      "@id": "dtmi:digitaltwins:org:archive:download:rfidsmartlibrary:RfidSmartLibraryowl:LibraryItemGateCheckSensorTest:LibraryItemDataProperty:schema:DateTime;1",
      "displayName": {
       "en": "DateTime"
      },
      "name": "DateTime",
      "schema": "dateTime"
     }
    ]
   },
   "writable": false
  }
 ],
 "displayName": {
  "en": "LibraryItemGateCheckSensorTest"
 },
 "@context": [
  "dtmi:iotcentral:context;2",
  "dtmi:dtdl:context;2"
 ]
}

I used the same template to create a digital twin named "LibraryItemGateCheckSensorTest02".

This is an example of twin LibraryItemDataProperty update using json:

[
  {
    "op": "add",
    "path": "/LibraryItemDataProperty",
    "value": {
      "ItemId": "USB 2364",
      "Description": "”*Re-inventare la famiglia : guida teorico-pratica per i professionisti dell'educazione / a cura di Laura Formenti Santarcangelo di Romagna : Maggioli, 2016.. XXII, 451 p. ; 21 cm",
      "Location": "Main Gate",
      "DateTime": "2023-05-23T14:10:00"
    }
  }
]



and here is the code i developed to update LibraryItemDataProperty from property using Azure Service Bus queue data from an Iot Central device that send telemetry. DigitalTwinsManager.cs

using Azure;
using Azure.Core.Pipeline;
using Azure.DigitalTwins.Core;
using Azure.Identity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
namespace RfidSmartLibraryConsoleApp.DigitalTwins
{
    //public delegate void QueryResult(BasicDigitalTwin dt);
    public class DigitalTwinsManager
    {
        private static readonly string adtInstanceUrl = "https://xxxxx.api.weu.digitaltwins.azure.net";
        private DigitalTwinsClient client;
        public DigitalTwinsManager()
        {
            Connect();
        }
        public void Connect()
        {
            var cred = new DefaultAzureCredential();
            client = new DigitalTwinsClient(new Uri(adtInstanceUrl), cred);
        }
        
        public void UpdateDigitalTwinProperty(string twinId, string property, object value)
        {
            
            JsonPatchDocument patch = null;
            try
            {
                patch = new JsonPatchDocument();
                patch.AppendAdd("/" + property, value);
                
                client.UpdateDigitalTwin(twinId, patch);
            }
            catch (RequestFailedException)
            {
            }
            
        }
        
        public void ManagedConnect(string appId)
        {
            HttpClient httpclient = new HttpClient();
            var cred = new ManagedIdentityCredential(appId);
            client = new DigitalTwinsClient(new Uri(adtInstanceUrl),
                cred, new DigitalTwinsClientOptions { Transport = new HttpClientTransport(httpclient) });
        }
        
        
        public DigitalTwinsManager(string AppId)
        {
            ManagedConnect(AppId);
        }
        
    }
}



and IoTCentralTrigger.cs

using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RfidSmartLibraryConsoleApp.DigitalTwins;
using Azure.DigitalTwins.Core;
using Azure;

namespace SmartBuildingSensorUpdater
{
    public class IoTCentralTrigger
    {
        const string adtAppId = "https://digitaltwins.azure.net";
        
        const string queueName = "iotcentral";


        [FunctionName("IoTCentralTriggerTest")]
        public void Run([ServiceBusTrigger(queueName,
                                            Connection ="ServiceBusConnection")]
                                            Message message,
                                            ILogger log)
        {


            /*
                Data Example to update

               "LibraryItemData": {
                   "ItemId": "Eum suscipit accusantium aut porro.",
                   "Description": "Pariatur autem laborum.",
                   "Location": "Architecto necessitatibus exercitationem ut similique.",
                   "DateTimeValue": "2024-03-03T19:01:43.522Z"
               },

           Message body Example:
           Received twinId: LibraryItemGateCheckSensorTest02
           Received iotcentral-message-source: telemetry
           Received message body: {
               "applicationId": "xxx-34bf-x150-9ax4-fcxxxe86f84",
               "deviceId": "LibraryItemGateCheckSensorTest02",
               "enqueuedTime": "2023-05-22T16:56:35.12Z",
               "enrichments": {},
               "messageProperties": {
                "iothub-creation-time-utc": "2023-05-22T16:56:35.096Z"
           },
           "messageSource": "telemetry",
           "schema": "default@v1",
           "telemetry": {
               "LibraryItemData": {
               "DateTimeValue": "2024-02-13T01:11:52.592Z",
               "Description": "Quo temporibus vero totam.",
                "ItemId": "Nisi quas temporibus doloribus id.",
                "Location": "Impedit porro quidem omnis ex et in et voluptatem."
               }
            },
            "templateId": "dtmi:hwzomnev:mt5wwsum"
           }

           Json example to update twin LibraryItemDataProperty :

            [
                {
                     "op": "add",
                      "path": "/LibraryItemDataProperty",
                      "value": {
                        "ItemId": "USB 2364",
                        "Description": "”*Re-inventare la famiglia : guida teorico-pratica per i professionisti dell'educazione / a cura di Laura Formenti Santarcangelo di Romagna : Maggioli, 2016.. XXII, 451 p. ; 21 cm",
                        "Location": "Main Gate",
                        "DateTime": "2023-05-23T14:10:00"
                       }
                }
            ]

            */

            Console.WriteLine("*********************************************************");

            string twinId = message.UserProperties["iotcentral-device-id"].ToString();
            //Console.WriteLine($"Received twinId: {twinId}");

            string iotcentralMessageSource = message.UserProperties["iotcentral-message-source"].ToString();
            //Console.WriteLine($"Received iotcentral-message-source: {iotcentralMessageSource}");

            var body = message.Body;
            var text = Encoding.UTF8.GetString(body);
            var messagebody = JsonConvert.DeserializeObject(text);
            //Console.WriteLine($"Received message body: {messagebody}");

            string value = Encoding.ASCII.GetString(message.Body, 0, message.Body.Length);
            //Console.WriteLine($"Received value: {value}");

            var bodyProperties = (JObject)JsonConvert.DeserializeObject(value);
            //Console.WriteLine($"Received bodyProperties: {bodyProperties}");

            JToken LibraryItemDataToken = bodyProperties["telemetry"]["LibraryItemData"];
           
            Console.WriteLine($"Received LibraryItemDataToken: {LibraryItemDataToken}");

            JToken LibraryItemDataItemIdToken = bodyProperties["telemetry"]["LibraryItemData"]["ItemId"];
            string ItemId = LibraryItemDataItemIdToken.Value<string>();
            //Console.WriteLine($"Received ItemId: {ItemId}");

            JToken LibraryItemDataDescriptionToken = bodyProperties["telemetry"]["LibraryItemData"]			["Description"];
            string Description = LibraryItemDataDescriptionToken.Value<string>();
            //Console.WriteLine($"Received Description: {Description}");

            JToken LibraryItemLocationToken = bodyProperties["telemetry"]["LibraryItemData"]["Location"];
            string Location = LibraryItemLocationToken.Value<string>();
            //Console.WriteLine($"Received Location: {Location}");

            JToken LibraryItemDateTimeToken = bodyProperties["telemetry"]["LibraryItemData"]["DateTimeValue"];
            string DateTimeValue = LibraryItemDateTimeToken.Value<string>();
            //Console.WriteLine($"Received DateTimeValue: {DateTimeValue}");

            Console.WriteLine("*********************************************************");
             
            
            log.LogInformation(string.Format("Sensor ItemID:{0}", ItemId));
            log.LogInformation(string.Format("Sensor Description:{0}", Description));
            log.LogInformation(string.Format("Sensor Location:{0}", Location));
            log.LogInformation(string.Format("Sensor DateTime:{0}", DateTimeValue));

       

        DigitalTwinsManager manager = new DigitalTwinsManager(adtAppId);
        
        Console.WriteLine($"Twin {twinId} property updating..");
        manager.UpdateDigitalTwinProperty(twinId, "LibraryItemDataProperty", LibraryItemDataDescriptionToken);
                    }
    }
}

it's not clear to me how to manage object property "LibraryItemDataProperty" in UpdateDigitalTwinProperty function as indicated in json file. Look at the following snapshot about running:

immagine

So my code doesn't update the twin object property.

I think the issue is in value of patch.AppendAdd("/" + property, value) but i can't manage value that should be like this:


"value": {
      "ItemId": "USB 2364",
      "Description": "”*Re-inventare la famiglia : guida teorico-pratica per i professionisti dell'educazione / a cura di Laura Formenti Santarcangelo di Romagna : Maggioli, 2016.. XXII, 451 p. ; 21 cm",
      "Location": "Main Gate",
      "DateTime": "2023-05-23T14:10:00"
    }

i haven't found an example like this one in https://learn.microsoft.com/en-us/azure/digital-twins/how-to-manage-twin.

Any help is welcome.

Thanks in advance.

Guido

Azure Digital Twins
Azure Digital Twins
An Azure platform that is used to create digital representations of real-world things, places, business processes, and people.
220 questions
Azure Service Bus
Azure Service Bus
An Azure service that provides cloud messaging as a service and hybrid integration.
553 questions
Azure IoT Central
Azure IoT Central
An Azure hosted internet of things (IoT) application platform.
352 questions
{count} votes

Accepted answer
  1. LeelaRajeshSayana-MSFT 13,546 Reputation points
    2023-05-24T21:49:27.14+00:00

    Hi @GuidoL Greetings! I have tested an approach to update a property of type Object and found a way to accomplish this. Here is my Azure Digital Twin template I have used to test the update.

    {
        "@id": "dtmi:example:ComplexProperty;1",
        "@type": "Interface",
        "displayName": "SampleModel",
        "contents": [
            {
                "@type": "Property",
                "name": "accelerometer",
                "schema": {
                    "@type": "Object",
                    "fields": [
                        {
                            "name": "x",
                            "schema": "double"
                        },
                        {
                            "name": "y",
                            "schema": "double"
                        },
                        {
                            "name": "z",
                            "schema": "double"
                        }
                    ]
                }
            },
            {
                "@type": "Property",
                "name": "data",
                "schema": "string"
            }
        ],
        "@context": "dtmi:dtdl:context;2"
    }
    
    
    
    

    The property accelerometer is similar to your LibraryItemDataProperty. Since the property is of type object, we need to provide a patch of type object. To achieve this, I have created a class to extract data and trasnform the data with desired fields. Here is the class I defined that corresponds to accelerometer property.

    public class Accelerometer
    {
        public double x { get; set; }
        public double y { get; set; }
        public double z { get; set; }
    }
    

    I have then followed the below approach to push the data to Azure Digital Twin.

    int x_v = 5;
    int y_v = 7;
    int z_v = 9;
    //String to hold the telemetry data
    string accelerometerJson = $@"{{""x"":""{x_v}"",""y"":""{y_v}"",""z"":""{z_v}""}}";
    //Convert the String to match the object declared in the template
    Accelerometer myObject = JsonConvert.DeserializeObject<Accelerometer>(accelerometerJson);
    updateTwinData.AppendAdd("/accelerometer", myObject);
    client.UpdateDigitalTwin(twinData.Id, updateTwinData);
    
    

    Executing the code patched the values to the ADT without any issues. Hope this helps. Please let us know if you run into any issues or need further assistance. We would be glad to help you.


    If the response helped, please do click Accept Answer and Yes. Doing so would help other community members with similar issue identify the solution. I highly appreciate your contribution to the community.


0 additional answers

Sort by: Most helpful