阿里云物联网 .NET Core 客户端 | CZGL.AliIoTClient:4. 设备上报属性

内容纲要

文档目录:


 

设备自身 CPU 温度、电源输入电压、内存使用率等,以及接入到设备的传感器如温度传感器、光敏传感器等,这些硬件的数据输出即是 属性 。
设备将这些硬件的数据上传到阿里云物联网平台,实时显示这些设备的状态和实测数据,这个过程是 上传设备属性 。


1)定义物模型

在阿里云物联网控制台,点击 产品 -> 功能定义 -> 添加自定义功能
填入一下内容:

功能类型:属性  
功能名称: CPU温度  
标识符: cpu_temperature  
数据类型: float (单精度浮点型)  
取值范围:0-120  
步长:    0.1
单位:  摄氏度 / °C  
读写类型:只读  

再定义一个属性:

功能类型:属性  
功能名称: 格力空调温度  
标识符: gree_temperature  
数据类型: float (单精度浮点型)  
取值范围:0-35  
步长:    0.1
单位:  摄氏度 / °C  
读写类型:读写

注意的是,表示符是区分大小写的,相当于 C# 中的变量,笔者这里建议统一使用小写,具体原因后面说明。
注意:读写类型,一个只读、一个读写。


2)编写模型

前面说过, Alink json 是阿里云定义具有一定格式的 Json ,
因此这些属性数据是以 Json 形式上传。在 C# 中,可以通过 类 快速生成 Json 。

参数 类型 说明
id string 消息ID号,在这个设备的生涯中,ID应当是唯一的。可以使用时间戳或guid
version string 协议版本号,目前协议版本号为1.0。固定 “1.0” 即可
params Object 属性数据,里面包含多个属性对象,每个属性对象包含上报时间(time)和上报的值(value)。
time long 属性上报时间。
value object 上报的属性值。
method string 固定取值 thing.event.property.post

那么,我们要编写一个类,存储信息,然后转为 Alink json 上传到阿里云物联网服务器。在编写这个模型前,预览要生成的 Alink json :

{
  "id": "123456789",
  "version": "1.0",
  "params": {
    "cpu_temperature": {
      "value": 58.6,
      "time": 1524448722000
    },
    "gree_temperature": {
      "value": 26.6,
      "time": 1524448722000
    }
  },
  "method": "thing.event.property.post"
}

我们只需关注 params 部分的编写即可。

在控制台程序中,新建一个类 TestModel

   public class TestModel
    {
        public string id { get { return DateTime.Now.Ticks.ToString(); } set { } }
        public string version { get { return "1.0"; } set { } }
        public Params @params { get; set; }
        public class Params
        {
            /*
             * 
             */
        }
        public string @method { get { return "thing.event.property.post"; } set { } }
    }

这样定义后,我们使用时,只需定义 params 部分即可, id、version等,不需要自己动态取值,做重复劳动。
上面有个 code>@params ,这是因为 params 是 C# 的关键字,命名字段时为了取消冲突所以加个 code>@。

根据我们在阿里云物联网控制台定义的 属性 ,继续补充内容:

   public class TestModel
    {
        public string id { get { return DateTime.Now.Ticks.ToString(); } set { } }
        public string version { get { return "1.0"; } set { } }
        public Params @params { get; set; }
        public class Params
        {
            public Cpu_temperature cpu_temperature { get; set; }
            public Gree_temperature gree_temperature { get; set; }
            public class Cpu_temperature
            {
                public float value{ get; set; }
                public long time { get; set; }
            }
            public class Gree_temperature
            {
                public float value { get; set; }
                public long time { get; set; }
            }

        }
        public string @method { get { return "thing.event.property.post"; } set { } }
    }

问题是,这样写还不行,因为还没有给 TestModel 里的类进行实例化。
我们可以利用 构造函数 对里面的引用类型进行实例化,当然亦可编写依赖注入容器。。

   public class TestModel
    {
        public string id { get { return DateTime.Now.Ticks.ToString(); } set { } }
        public string version { get { return "1.0"; } set { } }
        public Params @params { get; set; }

        public TestModel()
        {
            @params = new Params();
        }
        public class Params
        {
            public Cpu_temperature cpu_temperature { get; set; }
            public Gree_temperature gree_temperature { get; set; }

            public Params()
            {
                cpu_temperature = new Cpu_temperature();
                gree_temperature = new Gree_temperature();
            }
            public class Cpu_temperature
            {
                public float value{ get; set; }
                public long time { get; set; }
            }
            public class Gree_temperature
            {
                public float value { get; set; }
                public long time { get; set; }
            }

        }
        public string method { get { return "thing.event.property.post"; } set { } }
    }

3)上传设备属性数据

编写控制台程序,引入 CZGL.AliIoTClient ,编写基础代码(请替换 DeviceOptions 的信息):

        static AliIoTClientJson client;
        static void Main(string[] args)
        {
            // 创建客户端
            client = new AliIoTClientJson(new DeviceOptions
            {
                ProductKey = "a1A6VVt72pD",
                DeviceName = "json",
                DeviceSecret = "7QrjTptQYCdepjbQvSoqkuygic2051zM",
                RegionId = "cn-shanghai"
            });

            // 设置要订阅的Topic、运行接收内容的Topic
            string[] topics = new string[] { client.CombineHeadTopic("get") };
            // 使用默认事件
            client.UseDefaultEventHandler();
            // 连接服务器
            client.ConnectIoT(topics,null,60);
            ToServer();    // 自定义方法,后面说明
            Console.ReadKey();
        }

再 Program 类中,编写一个方法用来收集属性数据、上传属性数据:

        public static void ToServer()
        {
            // 实例化模型
            TestModel model = new TestModel();

            // 设置属性值
            model.@params.cpu_temperature.value = 56.5F;
            model.@params.cpu_temperature.time =AliIoTClientJson.GetUnixTime();
            // 低碳环境、节约资源,从你我做起,夏天空调不低于 26°
            model.@params.gree_temperature.value=26.0F;
            model.@params.gree_temperature.time=AliIoTClientJson.GetUnixTime();

            // 上传属性数据
            client.Thing_Property_Post<TestModel>(model,false);
        }

启动控制台应用,在阿里云物联网控制台,打开设备,点击 运行状态 ,即可看到上传的属性数据。 文章后面会详细说明 CZGL.AliIoTClient 关于属性上传的具体情况。

当然,这样的数据只是固定赋值的,这里只是演示,具体数据需要开发者采集。下面给出一些模拟数据的方法。


4)模拟数据

笔者编写了三个数据模拟方法:
不需要理会里面是怎么写的,仅是个模拟数据的工具而已,你也可以自己编写相应的模拟数据方法。 里面有四个参数,对应:原始值、最小值、最大值、波动范围。

    /// <summary>
    /// 模拟数据
    /// </summary>
    public static class DeviceSimulate
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="original">原始数据</param>
        /// <param name="range">波动范围</param>
        /// <param name="min">最小值</param>
        /// <param name="max">最大值</param>
        /// <returns></returns>
        public static int Property(ref int original, int min, int max, int range)
        {
            int num = (new Random()).Next(0, range + 1);
            bool addorrm;
            if (original + num > max || original > max)
                addorrm = false;
            else if (original < min || original - num < min)
                addorrm = true;
            else addorrm = ((new Random()).Next(1, 3) > 1) ? true : false;

            if (addorrm == true)
                original += num;
            else
                original -= num;
            return original;
        }

        public static float Property(ref float original, float min, float max, int range = 8)
        {
            original = float.Parse(original.ToString("#0.00"));
            float num = float.Parse(((new Random()).NextDouble() / range).ToString("#0.00"));
            bool addorrm;
            if (original + num > max || original > max)
                addorrm = false;
            else if (original < min || original - num < min)
                addorrm = true;
            else addorrm = ((new Random()).Next(1, 3) > 1) ? true : false;

            if (addorrm == true)
                original += num;
            else
                original -= num;
            original = float.Parse(original.ToString("#0.00"));
            return original;
        }

        public static double Property(ref double original, double min, double max, int range = 8)
        {
            original = double.Parse(original.ToString("#0.0000"));
            double num = double.Parse(((new Random()).NextDouble() / range).ToString("#0.0000"));
            bool addorrm;
            if (original + num > max || original > max)
                addorrm = false;
            else if (original < min || original - num < min)
                addorrm = true;
            else addorrm = ((new Random()).Next(1, 3) > 1) ? true : false;

            if (addorrm == true)
                original += num;
            else original -= num;
            original = double.Parse(original.ToString("#0.0000"));
            return original;
        }
    }

int 模拟数据
range 是指每次生成 [0,range] 范围的增/减量,
例如 初始值 56 , range = 2 ,那么可能 56±0 或 56±1 或 56±2 , 是增还是减,是随机的。但是设置 min 、 max 后,最后生成的值会在此范围内波动。

float、double 模拟数据
对应 float、double,range 的值越大,波动范围越小。默认 range = 8,大概就是每次 0.1 的波动范围。
其中,float 小数保留两位, double 小数保留 4 位,
需要更高或减少小数位数,修改一下 ...ToString("#0.0000")

模拟属性数据
接下来我们模拟一下两个属性的数据。

在 Program 中定义两个变量存储 cpu 和 空调 数据。

        static float cpu_temperature = 50.0F;
        static float gree_temperature = 26.0F;

修改 ToServer() 方法

        public static void ToServer()
        {
            // 实例化模型
            TestModel model = new TestModel();

            // 设置属性值
            model.@params.cpu_temperature.value = DeviceSimulate.Property(ref cpu_temperature, 40, 60, 8);
            model.@params.cpu_temperature.time = AliIoTClientJson.GetUnixTime();
            // 低碳环境、节约资源,从你我做起,夏天空调不低于 26°
            model.@params.gree_temperature.value = DeviceSimulate.Property(ref gree_temperature, 40, 60, 8); ;
            model.@params.gree_temperature.time = AliIoTClientJson.GetUnixTime();

            // 上传属性数据
            client.Thing_Property_Post<TestModel>(model, false);
        }

在 Main() 方法里增加代码:

            // 定时上传数据
            while (true)
            {
                ToServer();
                Thread.Sleep(1000);
            }

至此,已经基本完成。

完整代码如下:

    class Program
    {
        static AliIoTClientJson client;
        static void Main(string[] args)
        {
            // 创建客户端
            client = new AliIoTClientJson(new DeviceOptions
            {
                ProductKey = "a1A6VVt72pD",
                DeviceName = "json",
                DeviceSecret = "7QrjTptQYCdepjbQvSoqkuygic2051zM",
                RegionId = "cn-shanghai"
            });

            // 设置要订阅的Topic、运行接收内容的Topic
            string[] topics = new string[] { client.CombineHeadTopic("get") };
            // 使用默认事件
            client.UseDefaultEventHandler();
            // 连接服务器
            client.ConnectIoT(topics, null, 60);
            // 定时上传数据
            while (true)
            {
                ToServer();
                Thread.Sleep(1000);
            }

            Console.ReadKey();
        }
        static float cpu_temperature = 50.0F;
        static float gree_temperature = 26.0F;
        public static void ToServer()
        {
            // 实例化模型
            TestModel model = new TestModel();

            // 设置属性值
            model.@params.cpu_temperature.value = DeviceSimulate.Property(ref cpu_temperature, 40, 60, 8);
            model.@params.cpu_temperature.time = AliIoTClientJson.GetUnixTime();
            // 低碳环境、节约资源,从你我做起,夏天空调不低于 26°
            model.@params.gree_temperature.value = DeviceSimulate.Property(ref gree_temperature, 40, 60, 8); ;
            model.@params.gree_temperature.time = AliIoTClientJson.GetUnixTime();

            // 上传属性数据
            client.Thing_Property_Post<TestModel>(model, false);
        }

        /// <summary>
        /// 模拟数据
        /// </summary>
        public static class DeviceSimulate
        {
            /// <summary>
            /// 
            /// </summary>
            /// <param name="original">原始数据</param>
            /// <param name="range">波动范围</param>
            /// <param name="min">最小值</param>
            /// <param name="max">最大值</param>
            /// <returns></returns>
            public static int Property(ref int original, int min, int max, int range)
            {
                int num = (new Random()).Next(0, range + 1);
                bool addorrm;
                if (original + num > max || original > max)
                    addorrm = false;
                else if (original < min || original - num < min)
                    addorrm = true;
                else addorrm = ((new Random()).Next(1, 3) > 1) ? true : false;

                if (addorrm == true)
                    original += num;
                else
                    original -= num;
                return original;
            }

            public static float Property(ref float original, float min, float max, int range = 8)
            {
                original = float.Parse(original.ToString("#0.00"));
                float num = float.Parse(((new Random()).NextDouble() / range).ToString("#0.00"));
                bool addorrm;
                if (original + num > max || original > max)
                    addorrm = false;
                else if (original < min || original - num < min)
                    addorrm = true;
                else addorrm = ((new Random()).Next(1, 3) > 1) ? true : false;

                if (addorrm == true)
                    original += num;
                else
                    original -= num;
                original = float.Parse(original.ToString("#0.00"));
                return original;
            }

            public static double Property(ref double original, double min, double max, int range = 8)
            {
                original = double.Parse(original.ToString("#0.0000"));
                double num = double.Parse(((new Random()).NextDouble() / range).ToString("#0.0000"));
                bool addorrm;
                if (original + num > max || original > max)
                    addorrm = false;
                else if (original < min || original - num < min)
                    addorrm = true;
                else addorrm = ((new Random()).Next(1, 3) > 1) ? true : false;

                if (addorrm == true)
                    original += num;
                else original -= num;
                original = double.Parse(original.ToString("#0.0000"));
                return original;
            }
        }

    }

运行控制台程序,然后打开阿里云物联网控制台,查看设备的运行状态,打开 自动刷新 ,查看数据变化。

如果你觉得每次波动得范围太大,可以把 range 改大一些,如果你觉得数据不稳定,
可以把 min – max 的范围改小一些,模拟的数据值将在此范围波动。


5)设备属性 – CZGL.AliIoTClient

首先要说明,产品创建前,需要设置为 Alinkjson/透传 产品,
因此 CZGL.AliIoTClient 设置了两个客户端类。

类名 说明
AliIoTClientJson 以Alink json形式上传数据
AliIoTClientBinary 以透传形式上传数据

这两个类,仅在 属性、事件、服务 三个功能中数据上传形式有差别,连接服务器、普通Topic等其它数据的使用是完全一致的。
一个产品只能定义一种上传数据的形式。

CZGL.AliIoTClient 中上传属性的方法(Alink json):

// 不需要SDK处理任何中间过程,直接把数据上传。
// 那你需要先将数据存储到json中,在转成byte[],由SDK发送。
public int Thing_Property_Post(byte[] json)

// 由SDK帮你发送原始的json,是否需要将json转为小写再发送,默认 true
public int Thing_Property_Post(string json, 
                               [bool isToLwer = True])

// 设置要发送的json;是否转为小写;设置编码格式,为空则为UTF8
public int Thing_Property_Post(string json, 
                               [bool isToLwer = True], 
                               [System.Text.Encoding encoding = null])

// 直接传入模型,什么都不需要管,SDK转换后上传
public int Thing_Property_Post<TModel>(TModel model, 
                               [bool isToLower = True])

获取 UNIX 时间: 由于阿里云要求上传的属性数据等,要带上 Unix 时间,所以笔者一并写在 CZGL.AliIoTClient 了。

public static long GetUnixTime()

使用示例参考上面的过程。

透传
如果你想使用透传,则使用 AliIoTClientBinary 类,

// 设备上传属性--透传
public int Thing_Property_UpRaw(byte[] bytes)

// 设备上传属性--透传,转为 Base 64位加密后上传
public int Thing_Property_UpRawToBase64(byte[] bytes, 
                        [System.Text.Encoding encoding = null])

6)关于透传

透传以二进制报文形式上传,例如 0x020000007b00 ,这里是 16 进制,每两位一个字节。
如果是 2进制 ,则是 8位 一个字节。

透传需要在阿里云物联网控制台创建 透传 产品后,设置脚本,将透传数据 转为 Alink json。
透传数据是自定义的,以字节为单位,其中有5个字节为特定字节,以字节位进行拆分的。

记住,是以字节为单位。

透传数据格式标准:

字段 字节数
帧类型 1字节
请求ID 4字节
属性数据 N个字节

帧类型:

值(16进制) 说明
0x00 属性上报
0x01 属性设置
0x02 上报数据返回结果
0x03 属性设置设备返回结果
0xff 未知的命令

举例说明

很多人是直接把 10进制 或 16进制 直接转换成 2进制 。
例如 0x020000007b00,转为 2进制 :100000000000000000000000000111101100000000。 但是这样是错误的。

以上面 cpu 和 空调温度 举例,要上传属性数据,帧类型为 0x00。

属性 10进制 16进制 2进制 划一下2进制
cpu_temperature 56 38 00111000 00 11 10 00
gree_temperature 26 1a 00011010 00 01 10 10

应当这样拆分和设置值:

字节类转 字节数 16进制 2进制
进制表示 0x
帧类型 1字节 00 00000000
ID 4字节 00 00 00 7b 00000000 00000000 00000000 01111011
cpu_temperature 1 字节 38 00111000
gree_temperature 1 字节 1a 00011010

16进制数据:
0x000000007b381a

2进制数据:
00000000000000000000000000000000011110110011100000011010

将 16进制 或 2进制 的数据存储到 byte[] 变量中,切记要强制转换。 存储时,一个 byte 为一个字节,M个字节,则 byte[M]。

存储:
使用 16进制 存储透传数据,2进制弄不来的。 🤣🤣🤣

PS:新版本 C# 支持了二进制字面量。
有些同学非要用 2进制 存储,反正我是弄不来,用 二进制 数值 存储,这个触发我的知识盲区了。

示例(仅对 AliIoTClientBinary 客户端有效):

            // 存储透传数据
            byte[] b = new byte[7];
            b[0] = 0x00;
            b[1] = 0x00;
            b[2] = 0x00;
            b[3] = 0x00;
            b[4] = 0x7b;
            b[5] = 0x38;
            b[6] = 0x1a;

            // 上传透传数据
            client.Thing_Property_UpRaw(b);

如果上报属性,要求 请输入二进制数据Base64编码后的字符串,可以使用

            byte[] b = new byte[7];
            b[0] = 0x00;
            b[1] = 0x00;
            b[2] = 0x00;
            b[3] = 0x00;
            b[4] = 0x7b;
            b[5] = 0x38;
            b[6] = 0x1a;

            // client.Thing_Property_UpRaw(b);
            client.Thing_Property_UpRawToBase64(b);

透传数据的坑很多,这里 CZGL.AliIoTClient 只提供如何处理数据和上传数据,云端的脚本解析请参考
https://help.aliyun.com/document_detail/114621.html?spm=a2c4g.11186623.2.13.209b65b9Q9z0Nx#concept-185365


7)后续说明

其实,每次上传服务器都会作出响应,CZGL.AliIoTClient 默认不接收这些响应信息。
你可以使用 OpenPropertyPostReply() 接收设备属性上传后服务器的响应,应当在连接服务器前使用此方法
使用 Close.PropertyPostReply() 取消接收设备属性上传后服务器的响应。

示例:

            // 。。。
            client.ClosePropertyPostReply();

            // 连接服务器
            client.ConnectIoT(topics, null, 60);

上传属性数据,可以分开上传,不需要每次都要上传全部的属性。需要更新哪个属性,就上传这个属性。

点赞
  1. zyprexa pills说道:

    Thanks for the recommendations on credit repair on this amazing site. A few things i would tell people is always to give up the particular mentality they can buy today and shell out later. Like a society we all tend to try this for many issues. This includes getaways, furniture, and also items we wish. However, you should separate the wants out of the needs. While you're working to raise your credit score make some sacrifices. For example it is possible to shop online to save cash or you can visit second hand retailers instead of pricey department stores regarding clothing.

发表评论

邮箱地址不会被公开。 必填项已用*标注

You must enable javascript to see captcha here!