【问题标题】:Azure IoT Hub - How to get what's newer of desired vs reported property in device twins?Azure IoT 中心 - 如何在设备孪生中获取更新的所需属性和报告属性?
【发布时间】:2020-07-20 02:18:26
【问题描述】:

我正在使用 .NET Core 3 从 Azure IoT 中心的设备孪生中读取数据。我想获取属性 X,并且该属性一如既往地存储在所需和报告的属性中。我想买一个更新的。此信息写入元数据中。

我的问题是,这是否可以仅通过 IoT 中心查询语言实现,还是我必须从期望和报告中获取并自己检查?

【问题讨论】:

    标签: azure iot azure-iot-hub


    【解决方案1】:

    Azure IoT Hub Query Language 仅支持 SQL 语句的子集,因此以下示例(device1 和孪生属性 color)显示了缺少 CASE 语句的解决方法:

    1. 查询字符串以获取所需的属性作为 lastUpdated:

      querystring = $"SELECT devices.properties.desired.color FROM devices WHERE deviceId = 'device1' and devices.properties.desired.$metadata.color.$lastUpdated > devices.properties.reported.$metadata.color.$lastUpdated";
      
    2. 如果返回值为空,我们要进行第二次查询,获取上报的属性如:

       querystring = $"SELECT devices.properties.reported.color FROM devices WHERE deviceId = 'device1' and devices.properties.reported.$metadata.color.$lastUpdated > devices.properties.desired.$metadata.color.$lastUpdated";
      
    3. 如果返回值仍然为空,则设备孪生中缺少我们想要的和/或报告的属性,或者 deviceId 错误。

    下面的代码sn-p展示了上述用法的一个例子:

    using Microsoft.Azure.Devices;
    using System.Linq;
    using System;
    using System.Threading.Tasks;
    
    namespace ConsoleApp3
    {
        class Program
        {
            static string connectionString = "*****";
    
            static async Task Main(string[] args)
            {
                RegistryManager registryManager = RegistryManager.CreateFromConnectionString(connectionString);
    
                string deviceId = "device1";
                string propertyName = "color";
                string querystring = $"SELECT devices.properties.desired.{propertyName} FROM devices WHERE deviceId = '{deviceId}' and devices.properties.desired.$metadata.{propertyName}.$lastUpdated > devices.properties.reported.$metadata.{propertyName}.$lastUpdated";
    
                dynamic prop = null;
                for (int ii = 0; ii < 2; ii++)
                {
                    var query = registryManager.CreateQuery(querystring);
                    {
                        prop = (await query.GetNextAsJsonAsync())?.FirstOrDefault();
                        if (prop == null)
                            querystring = $"SELECT devices.properties.reported.{propertyName} FROM devices WHERE deviceId = '{deviceId}' and devices.properties.reported.$metadata.{propertyName}.$lastUpdated > devices.properties.desired.$metadata.{propertyName}.$lastUpdated";
                        else
                            break;
                    }
                }
                Console.WriteLine(prop ?? $"Not found property '{propertyName}' or device '{deviceId}'");
            }
        }
    }
    

    更新:

    在多个属性的情况下,我们必须通过获取的设备孪生实体中的代码单独检查每个属性。以下代码 sn -p 显示了此检查的示例:

    // multiple properties
    querystring = $"SELECT devices.properties FROM devices WHERE deviceId='{deviceId}'";
    var query2 = registryManager.CreateQuery(querystring);
    JObject prop2 = JObject.Parse((await query2.GetNextAsJsonAsync())?.FirstOrDefault());
    
    JToken desired = prop2.SelectToken("properties.desired");
    JToken reported = prop2.SelectToken("properties.reported");
    
    string pathLastUpdated = $"$metadata.{propertyName}.$lastUpdated";          
    
    var color = (DateTime)desired.SelectToken(pathLastUpdated) > (DateTime)reported.SelectToken(pathLastUpdated) ?
                (string)desired[propertyName] : (string)reported[propertyName];
    
    // more properties
    
    Console.WriteLine(color);
    

    另外,您可以创建一个扩展类来简化代码,请参见以下示例:

    public static class JObjectExtensions
    {
        public static T GetLastUpdated<T>(this JObject properties, string propertyName)
        {
            JToken desired = properties.SelectToken("properties.desired");
            JToken reported = properties.SelectToken("properties.reported");
            string pathLastUpdated = $"$metadata.{propertyName}.$lastUpdated";
    
            return (DateTime)desired.SelectToken(pathLastUpdated) > (DateTime)reported.SelectToken(pathLastUpdated) ?
                desired.SelectToken(propertyName).ToObject<T>() : reported.SelectToken(propertyName).ToObject<T>();
        }
    
        public static string GetLastUpdated(this JObject properties, string propertyName)
        {
            return GetLastUpdated<string>(properties, propertyName);
        }
    }
    

    上述扩展的以下用法显示了如何根据 lastUpdated 时间戳获取任何所需的与报告的属性:

    color = prop2.GetLastUpdated(propertyName);
    
    string color2 = prop2.GetLastUpdated("test.color");
    
    var test = prop2.GetLastUpdated<JObject>("test");
    
    string jsontext = prop2.GetLastUpdated<JObject>("test").ToString(Formatting.None);
    
    var test2 = prop2.GetLastUpdated<Test>("test");
    
    int counter = prop2.GetLastUpdated<int>("counter");
    

    请注意,在缺少属性的情况下会引发异常。

    【讨论】:

    • 谢谢。这是有趣的。但是,这些是对 IoT 中心的 1 次或 2 次调用。我希望只有 1 个电话。另外,我选择了很多属性,而不仅仅是一个属性,并且对于每个属性,我都想选择较新的报告与期望。有什么想法吗?
    • 我在回答中已经提到,Azure IoT Hub 查询语言中不支持 SQL 语句 CASE(或 IIF 函数),它将启用根据其 $ 选择多个属性Select 语句中的 lastUpdated 值。您应该在feedback.azure.com/forums/321918-azure-iot 中写下针对该要求的反馈,同时您可以使用代码实现的解决方法,请参阅我的更新。
    猜你喜欢
    • 1970-01-01
    • 2018-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-09
    • 1970-01-01
    • 1970-01-01
    • 2019-12-03
    相关资源
    最近更新 更多