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

简介: 阿里云物联网.NET Core客户端|CZGL.AliloTClient: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等,不需要自己动态取值,做重复劳动。


上面有个 @params ,这是因为 params 是 C# 的关键字,命名字段时为了取消冲突所以加个 @


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


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进制弄不来的。  

有些同学非要用 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);


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

相关实践学习
钉钉群中如何接收IoT温控器数据告警通知
本实验主要介绍如何将温控器设备以MQTT协议接入IoT物联网平台,通过云产品流转到函数计算FC,调用钉钉群机器人API,实时推送温湿度消息到钉钉群。
阿里云AIoT物联网开发实战
本课程将由物联网专家带你熟悉阿里云AIoT物联网领域全套云产品,7天轻松搭建基于Arduino的端到端物联网场景应用。 开始学习前,请先开通下方两个云产品,让学习更流畅: IoT物联网平台:https://iot.console.aliyun.com/ LinkWAN物联网络管理平台:https://linkwan.console.aliyun.com/service-open
相关文章
|
4月前
|
弹性计算
阿里云ECS部署openvpn server无法访问客户端私网地址。
阿里云openvpn服务端无法访问客户端网络,服务器可以ping通openvpn客户端地址,但无法访问客户端后面的网段,确认nat与路由都已添加,并且无任何问题,在服务端抓包发现并没有任何出去的报文,现在搞不定了。。。。。
|
6月前
|
对象存储
.net core 阿里云接口之拷贝文件
紧接上文, 1)[.net core 阿里云接口之获取临时访问凭证](https://developer.aliyun.com/article/1363447?spm=a2c6h.12873639.article-detail.7.2b0e5b1cpeWbZ5 ".net core 阿里云接口之获取临时访问凭证") 2)[.net core 阿里云接口之将指定的OSS文件下载到流](https://developer.aliyun.com/article/1363886 ".net core 阿里云接口之将指定的OSS文件下载到流") 本文继续阿里云接口调用,将指定的OSS文件下载到流。
32 0
|
6月前
|
C# 对象存储
.net core 阿里云接口之将指定的OSS文件下载到流
紧接上文,[.net core 阿里云接口之获取临时访问凭证](https://developer.aliyun.com/article/1363447?spm=a2c6h.13262185.profile.8.180876540j71A9 ".net core 阿里云接口之获取临时访问凭证") 本文继续阿里云接口调用,将指定的OSS文件下载到流。 直接上代码: ```csharp /// <summary> /// 将指定的OSS文件下载到流 /// </summary> /// <param name="args"></param> /// <returns></returns> [H
126 0
|
6月前
|
对象存储 数据安全/隐私保护 开发者
.net core 阿里云接口之获取临时访问凭证
假设您是一个移动App开发者,希望使用阿里云OSS服务来保存App的终端用户数据,并且要保证每个App用户之间的数据隔离。此时,您可以使用STS授权用户直接访问OSS。 使用STS授权用户直接访问OSS的流程如下: ![image.png](https://ucc.alicdn.com/pic/developer-ecology/j2ygdazy447va_7d767aa1db4047778a7b5c568b5d7c11.png) ## 1、关于秘钥等信息的申请见如下链接 [使用STS临时访问凭证访问OSS](https://help.aliyun.com/zh/oss/developer
89 0
|
6月前
|
负载均衡 网络协议 应用服务中间件
使用阿里云NLB获取客户端原地址
本文为您介绍NLB如何获取客户端真实IP,及通过Proxy Protocol获取客户端真实IP的场景和配置教程。
104 1
|
7月前
|
Web App开发 Android开发 iOS开发
阿里云无影APP客户端下载_无影云电脑下载
阿里云无影APP客户端下载_无影云电脑下载,阿里云无影云电脑APP下载链接入口,支持iOS、Android、Windows、macOS和Web客户端,阿里云百科分享阿里云无影云电脑APP下载链接
403 0
|
Web App开发 弹性计算 Android开发
阿里云无影云桌面客户端下载Win/Mac/iOS/安卓/Web端均支持
阿里云无影客户端下载系统Win/Mac/iOS/安卓/Web端均支持
3538 0
阿里云无影云桌面客户端下载Win/Mac/iOS/安卓/Web端均支持
|
3月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
38 0
|
30天前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
28 0
|
30天前
mvc.net分页查询案例——mvc-paper.css
mvc.net分页查询案例——mvc-paper.css
5 0

相关产品

  • 物联网平台