Unable to view result of pipeline which trasport a value from a sensor to digital twin and in log stream of Azure function trigger

Anonymous
2022-12-06T17:05:38.54+00:00

Hello,
i'm unable to view result of an example pipeline (i'm following code in "Hands On Azure Digital Twins")
which trasports a temperature value from a sensor to a digital twin and i'm also unable to have this value in the messages that are handled by the Azure function.
In Azure logstream i obtain:
2022-12-06T16:02:01 Welcome, you are now connected to log-streaming service. The default timeout is 2 hours. Change the timeout with the App Setting SCM_LOGSTREAM_TIMEOUT (in seconds).
2022-12-06T16:02:58.458 [Information] Host Status: {"id": "iotcentraltriggertest","state": "Running","version": "4.14.0.19631","versionDetails": "4.14.0+fc14864b4fc2a095f8cd92c7f0ca338dbeabd430","platformVersion": "99.0.7.639","instanceId": "20ea833e4800cbfe732d6cf6a72d75495c0bfee51a6cbaf98064fa891d38c638","computerName": "10-30-7-253","processUptime": 1960992,"functionAppContentEditingState": "Unknown","extensionBundle": {"id": "Microsoft.Azure.Functions.ExtensionBundle","version": "3.17.0"}}
2022-12-06T16:03:58.676 [Information] Host Status: {"id": "iotcentraltriggertest","state": "Running","version": "4.14.0.19631","versionDetails": "4.14.0+fc14864b4fc2a095f8cd92c7f0ca338dbeabd430","platformVersion": "99.0.7.639","instanceId": "20ea833e4800cbfe732d6cf6a72d75495c0bfee51a6cbaf98064fa891d38c638","computerName": "10-30-7-253","processUptime": 2021209,"functionAppContentEditingState": "Unknown","extensionBundle": {"id": "Microsoft.Azure.Functions.ExtensionBundle","version": "3.17.0"}}
2022-12-06T16:04:58.610 [Information] Host Status: {"id": "iotcentraltriggertest","state": "Running","version": "4.14.0.19631","versionDetails": "4.14.0+fc14864b4fc2a095f8cd92c7f0ca338dbeabd430","platformVersion": "99.0.7.639","instanceId": "20ea833e4800cbfe732d6cf6a72d75495c0bfee51a6cbaf98064fa891d38c638","computerName": "10-30-7-253","processUptime": 2081143,"functionAppContentEditingState": "Unknown","extensionBundle": {"id": "Microsoft.Azure.Functions.ExtensionBundle","version": "3.17.0"}}
2022-12-06T16:05:58.731 [Information] Host Status: {"id": "iotcentraltriggertest","state": "Running","version": "4.14.0.19631","versionDetails": "4.14.0+fc14864b4fc2a095f8cd92c7f0ca338dbeabd430","platformVersion": "99.0.7.639","instanceId":
and so on but there aren't informations about sensor (id and temperature)
Here is code i used to develope my project SmartBuildingSensorUpdater and publish Azure function
IoTCentralTrigger.cs
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SmartBuildingConsoleApp.DigitalTwins;
using System.Text;
namespace SmartBuildingSensorUpdater
{
public class IoTCentralTrigger
{
const string adtAppId = "https://digitaltwins.azure.net";
//const string adtAppId = "https://digitaltwinbook-test.api.weu.digitaltwins.azure.net";
const string queueName = "iotcentral";

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

        string sensorId = message.UserProperties["iotcentral-device-id"].ToString();  
        string value = Encoding.ASCII.GetString(message.Body, 0, message.Body.Length);  
        var bodyProperties = (JObject)JsonConvert.DeserializeObject(value);  
        JToken temperatureToken = bodyProperties["telemetry"]["temperature"];  
        float temperature = temperatureToken.Value<float>();  
        log.LogInformation(string.Format("Sensor Id:{0}", sensorId));  
        log.LogInformation(string.Format("Sensor Temperature:{0}", temperature));  


        DigitalTwinsManager manager = new DigitalTwinsManager(adtAppId);  
        manager.UpdateDigitalTwinProperty(sensorId, "temperature", temperature);  

    }  


 
}  

}
and this is code i used in DigitalTwinsManager.cs
using Azure;
using Azure.Core.Pipeline;
using Azure.DigitalTwins.Core;
using Azure.Identity;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
namespace SmartBuildingConsoleApp.DigitalTwins
{
public delegate void QueryResult(BasicDigitalTwin dt);
public class DigitalTwinsManager
{
private static readonly string adtInstanceUrl = "https://digitaltwinbook-test.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 CreateModel(string path)  
   {  
        using var modelStreamReader = new StreamReader(path);  
        string dtdl = modelStreamReader.ReadToEnd();  
        string[] dtdls = new string[] { dtdl };  

        client.CreateModels(dtdls);  
   }  
    */  

    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)  
        {  
        }  
        patch = new JsonPatchDocument();  
        patch.AppendAdd("/" + property, value);  
        client.UpdateDigitalTwin(twinId, patch);  
    }  
    public bool UpdateDigitalTwin(string twinId, string property, object value)  
    {  

        try  
        {  
            BasicDigitalTwin digitalTwin = client.GetDigitalTwin<BasicDigitalTwin>(twinId);  

            digitalTwin.Contents[property] = value;  

            client.CreateOrReplaceDigitalTwin<BasicDigitalTwin>(twinId, digitalTwin);  
        }  
        catch (RequestFailedException)  
        {  
            return false;  
        }  
        return true;  
    }  



    public bool CreateDigitalTwin(string twinId, string modelId)  
    {  
        BasicDigitalTwin digitalTwin = new BasicDigitalTwin();  
        digitalTwin.Metadata = new DigitalTwinMetadata();  
        digitalTwin.Metadata.ModelId = modelId;  
        digitalTwin.Id = twinId;  
        try  
        {  
            client.CreateOrReplaceDigitalTwin<BasicDigitalTwin>(twinId, digitalTwin);  
        }  
        catch  
        {  
            return false;  
        }  
        return true;  
    }  

    public bool DeleteDigitalTwin(string twinId)  
    {  
        try  
        {  
            client.DeleteDigitalTwin(twinId);  
        }  
        catch (RequestFailedException)  
        {  
            return false;  
        }  
        return true;  
    }  
    public Pageable<DigitalTwinsModelData> GetModels()  
    {  
        GetModelsOptions options = new GetModelsOptions();  

        return client.GetModels(options);  
    }  

    public DigitalTwinsModelData GetModel(string modelId)  
    {  
        try  
        {  
            return client.GetModel(modelId);  
        }  
        catch (RequestFailedException)  
        {  
            return null;  
        }  
    }  

    public BasicDigitalTwin GetDigitalTwin(string twinId)  
    {  
        try  
        {  
            return client.GetDigitalTwin<BasicDigitalTwin>(twinId);  
        }  
        catch (RequestFailedException)  
        {  
            return null;  
        }  
    }  
    public void DeleteModel(string modelId)  
    {  
        client.DeleteModel(modelId);  
    }  

    public bool CreateModel(string path)  
    {  
        return CreateModels(new string[] { path });  
    }  

    public bool CreateModels(string[] path)  
    {  
        List<string> dtdls = new List<string>();  


        foreach (string p in path)  
        {  
            using var modelStreamReader = new StreamReader(p);  
            string dtdl = modelStreamReader.ReadToEnd();  
            dtdls.Add(dtdl);  
        }  

        try  
        {  
            DigitalTwinsModelData[] models = client.CreateModels(dtdls.ToArray());  
        }  
        catch (RequestFailedException)  
        {  
            return false;  
        }  

        return true;  
    }  

    public string RelationshipId(string twinSourceId, string twinDestinationId)  
    {  
        return string.Format("{0}-{1}", twinSourceId, twinDestinationId);  
    }  
    public void CreateRelationship(string twinSourceId, string twinDestionationId, string description, Dictionary<string, object> properties = null)  
    {  
        string relationShipId = RelationshipId(twinSourceId, twinDestionationId);  

        BasicRelationship relationship = new BasicRelationship  
        {  
            Id = "buildingFloorRelationshipId",  
            SourceId = twinSourceId,  
            TargetId = twinDestionationId,  
            Name = description,  
            Properties = properties  
        };  

        try  
        {  
            client.CreateOrReplaceRelationship(twinSourceId, relationShipId, relationship);  
        }  
        catch (RequestFailedException)  
        {  
        }  
    }  

    public BasicRelationship GetRelationship(string twinSourceId, string twinDestinationId)  
    {  
        string relationShipId = RelationshipId(twinSourceId, twinDestinationId);  
        try  
        {  
            Response<BasicRelationship> relationship = client.GetRelationship<BasicRelationship>(twinSourceId, relationShipId);  

            return relationship.Value;  
        }  
        catch (RequestFailedException)  
        {  
            return null;  
        }  
    }  

    public Pageable<BasicRelationship> ListRelationships(string twinSourceId)  
    {  

        try  
        {  
            Pageable<BasicRelationship> relationships = client.GetRelationships<BasicRelationship>(twinSourceId);  

            return relationships;  
        }  
        catch (RequestFailedException)  
        {  
        }  
        return null;  
    }  

    public void DeleteRelationship(string twinSourceId, string twinDestinationId)  
    {  

        string relationShipId = RelationshipId(twinSourceId, twinDestinationId);  

        try  
        {  
            client.DeleteRelationship(twinSourceId, relationShipId);  
        }  
        catch (RequestFailedException)  
        {  
        }  

    }  

    public void UpdateRelationship(string twinSourceId, string twinDestionationId, string property, object value)  
    {  
        string relationShipId = RelationshipId(twinSourceId, twinDestionationId);  
        try  
        {  
            JsonPatchDocument patch = new JsonPatchDocument();  
            patch.AppendReplace("/" + property, value);  
            client.UpdateRelationship(twinSourceId, twinDestionationId, patch);  
        }  
        catch (RequestFailedException)  
        {  
        }  
    }  

    public Pageable<BasicDigitalTwin> QueryDigitalTwins(string query)  
    {  
        Pageable<BasicDigitalTwin> result = null;  
        try  
        {  
            result = client.Query<BasicDigitalTwin>(query);  
        }  
        catch (RequestFailedException)  
        {  
        }  

        return result;  
    }  

    public Pageable<Dictionary<string, BasicDigitalTwin>> Query(string query)  
    {  
        Pageable<Dictionary<string, BasicDigitalTwin>> result = null;  

        try  
        {  
            result = client.Query<Dictionary<string, BasicDigitalTwin>>(query);  
        }  
        catch (RequestFailedException)  
        {  
        }  

        return result;  

    }  

    public void QueryDigitalTwins(string query, QueryResult onQueryResult)  
    {  
        System.Threading.Tasks.Task task = System.Threading.Tasks.Task.Run(() => QueryDigitalTwinsAsync(query, onQueryResult));  

    }  

    public async void QueryDigitalTwinsAsync(string query, QueryResult onQueryResult)  
    {  
        AsyncPageable<BasicDigitalTwin> result = client.QueryAsync<BasicDigitalTwin>(query);  
        try  
        {  
            await foreach (BasicDigitalTwin dt in result)  
            {  
                onQueryResult(dt);  
            }  
        }  
        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);  
    }  

}  

}
/*
internal class sting
{
}
*/
-------
The device i used works fine.

Devices MXCHIP Getting Started Guide  

Device id: zayeb3nvrc

Connected
|
Last data received: 12/6/2022, 5:38:08 PM
Simulated
|
Organization: dtbdemotestsensors
And in iotcentral (dtbtestservicebus/iotcentral) | Service Bus Explorer
i can view messages like the following:
{
"applicationId": "4c672aae-76c0-406a-9df3-9cd21f7cf04b",
"deviceId": "zayeb3nvrc",
"enqueuedTime": "2022-12-06T10:35:30.038Z",
"enrichments": {},
"messageProperties": {
"iothub-creation-time-utc": "2022-12-06T10:35:29.997Z"
},
"messageSource": "telemetry",
"schema": "default@v1",
"telemetry": {
"accelerometerX": 68.86120334260477,
"accelerometerY": 32.050495837284885,
"accelerometerZ": 74.0419344495432,
"gyroscopeX": 59.42697287321133,
"gyroscopeY": 18.887035439914364,
"gyroscopeZ": 44.45292249773999,
"humidity": 39.671942707907945,
"magnetometerX": 32.11203331230923,
"magnetometerY": 63.28605316989453,
"magnetometerZ": 74.82918974192012,
"pressure": 11.418789184264247,
"temperature": 54.48341927880178
},
"templateId": "dtmi:azurertos:devkit:rv0pln3py"
}
Hovewer i haven't temperature information in log stream of function App IotCentralTriggerTest and in Azure Digital Twins Explorer if i run query button to refresh the Graph View the twin i created from this model
{
"@id": "dtmi:com:smartbuilding:Sensor;1",
"@type ": "Interface",
"@Георгий Георгиевский ": "dtmi:dtdl:context;2",
"displayName": "Sensor",
"contents": [
{
"@type ": "Property",
"name": "temperature",
"schema": "float",
"writable": true
}
]
}
which has the same name of device doesn't show temperature after the update.
I hope everything is clear
Available for clarification.
Thanks in advance.
Guido

Azure IoT
Azure IoT
A category of Azure services for internet of things devices.
382 questions
{count} votes

3 answers

Sort by: Most helpful
  1. Anonymous
    2022-12-17T16:01:32.917+00:00

    Hi @LeelaRajeshSayana-MSFT ,
    the code i posted is optimized for visual studio version i used; the reference of library you indicated it is not necessary as indicated by the editor. i consider the issue closed.
    it was a pleasure interacting with you.
    Thanks for your support!
    Guido

    1 person found this answer helpful.
    0 comments No comments

  2. LeelaRajeshSayana-MSFT 13,471 Reputation points
    2022-12-12T21:45:59.137+00:00

    Hi @Anonymous ,

    I see that you are receiving the data into your Service Bus queue but not the Azure Function. The Azure Function can be bound to Azure Service Bus Queue using a ServiceBusTrigger which accepts string parameter for queue item. I see that you are using a ServiceBusTrigger function that accepts Message. Could you please provide a reference to the resource which you might have used to build the Azure function.

    Please refer the following resource Azure Service Bus trigger for Azure Functions which guides you to build Azure function.

    Here is the code I have used to process the data from the service bus and push the data to the Azure digital twins.

    using System;  
    using System.Net.Http;  
    using System.Threading.Tasks;  
    using Azure;  
    using Azure.Core.Pipeline;  
    using Azure.DigitalTwins.Core;  
    using Azure.Identity;  
    using Microsoft.Azure.WebJobs;  
    using Microsoft.Azure.WebJobs.Host;  
    using Microsoft.Extensions.Logging;  
    using Newtonsoft.Json;  
    using Newtonsoft.Json.Linq;  
      
    namespace RajIoTCentral.Function  
    {  
        public class RajIoTCentralServiceBusQueue  
        {  
            private static readonly HttpClient httpClient = new HttpClient();  
            private static string adtServiceUrl = "https://RajDigitalTwin.api.eus.digitaltwins.azure.net";  
      
            [FunctionName("RajIoTCentralServiceBusQueue")]  
            public async Task RunAsync([ServiceBusTrigger("iotcentraldata", Connection = "RajIoTDataServiceBus_SERVICEBUS")] string myQueueItem, ILogger log)  
            {  
                log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem}");  
      
                JObject deviceMessage = (JObject)JsonConvert.DeserializeObject(myQueueItem);  
                try  
                {  
                    var temperature = deviceMessage["telemetry"]["temperature"];  
                    var deviceId = "TemperatureSensor";  
                    log.LogInformation($"Temperature is:{temperature.Value<double>()}");  
                    var credentials = new DefaultAzureCredential();  
                    DigitalTwinsClient client = new DigitalTwinsClient(  
                        new Uri(adtServiceUrl), credentials, new DigitalTwinsClientOptions  
                        { Transport = new HttpClientTransport(httpClient) });  
                    log.LogInformation($"ADT service client connection created.");  
                    var updateTwinData = new JsonPatchDocument();  
                    updateTwinData.AppendReplace("/Temperature", temperature.Value<double>());  
                    await client.UpdateDigitalTwinAsync(deviceId, updateTwinData);  
                }  
                catch (Exception ex) { log.LogInformation("No temperature in the telemetry data"); }  
      
            }  
        }  
    }  
    

    Note The value set for my RajIoTDataServiceBus_SERVICEBUS connection string parameter in local.settings.json file points to my service bus RootManageSharedAccessKey service access policy. Refer the image for more details.

    269812-screenshot-40.png

    Here is my local.settings.json from my Azure Function app in Visual Studio Code which points to the connection string

    271096-image.png

    After you deploy the function to the cloud, you have to look under the configuration and make sure this setting is reflected. If you do not find this connection parameter, make sure to Add a New Application setting with the same connection string name as specified in your local.settings.json file. Please refer the below image.

    269766-2022-12-12-16-42-05-rajservicebusqueuetrigger-micr.png

    You may need to modify the logic inside the try block to make sure you are capturing the temperature data from the telemetry correctly. Here is my telemetry data from the device to the IoT central. I would recommend debugging the code with Visual Studio code and understand the telemetry data you are receiving to extract temperature.

    269786-screenshot-42.png

    I have used a similar JSON model as yours to generate the Azure digital twin.

    269787-screenshot-43.png

    With the above Azure function code, I could see the temperate data in the function logs and see the events getting generated on the Azure digital twin. Kindly refer the below images

    269802-screenshot-44.png

    269746-screenshot-45.png

    ----------

    Kindly accept answer or upvote if the response is helpful so that it would benefit other community members facing the same issue. I highly appreciate your contribution to the community.


  3. Anonymous
    2022-12-15T12:34:48.68+00:00

    Hi @LeelaRajeshSayana-MSFT ,
    i finally solved!
    Look at the corrections i made to the code:

    using System;
    using System.Net.Http;
    using System.Threading.Tasks;
    using Azure.Core.Pipeline;
    using Azure.DigitalTwins.Core;
    using Azure.Identity;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Extensions.Logging;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;

    namespace AzureFunctionToDigitalTwin
    {
    public class TestIoTCentralServiceBusQueue
    {
    private static readonly HttpClient httpClient = new HttpClient();
    private static string adtServiceUrl = "https://digitaltwinbook-test.api.weu.digitaltwins.azure.net";

        [FunctionName("TestIoTCentralServiceBusQueue")]  
        public async Task RunAsync([ServiceBusTrigger("iotcentral", Connection = "TestIoTDataServiceBus_SERVICEBUS")] string messageQueue, ILogger log)  
        {  
            log.LogInformation($"C# ServiceBus queue trigger function processed message: {messageQueue}");  
    
            JObject deviceMessage = (JObject)JsonConvert.DeserializeObject(messageQueue);  
            try  
            {  
                var temperature = deviceMessage["telemetry"]["temperature"];  
                var deviceId = "TemperatureSensor";  
                log.LogInformation($"Temperature is:{temperature.Value<double>()}");  
                var credentials = new DefaultAzureCredential();  
                  
                DigitalTwinsClient client = new DigitalTwinsClient(  
                    new Uri(adtServiceUrl), credentials, new DigitalTwinsClientOptions  
                    { Transport = new HttpClientTransport(httpClient) });  
    
                log.LogInformation($"ADT service client connection created.");  
                var updateTwinData = new Azure.JsonPatchDocument();  
                updateTwinData.AppendReplace("/Temperature", temperature.Value<double>());  
                await client.UpdateDigitalTwinAsync(deviceId, updateTwinData);  
            }  
            catch (Exception) {   
                log.LogInformation("No temperature in the telemetry data");   
            }  
        }  
    }  
    

    }

    As mentioned I used the latest version of Microsoft Visual Studio Community 2022 (64 bit) (Current
    Version 17.4.3) with the following packages:

    270989-immagine.png

    So now i have no more the errors i indicated and code works fine.
    Look at the output about my device:

    271034-immagine.png

    Thanks so much for you support but how much effort to have the correct code with the right packages.

    if official documentation was clearer and more detailed life would be easier :-)

    Thanks again.
    Guido

    0 comments No comments