設備自身 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();
// 設置屬性值
[email protected]_temperature.value = 56.5F;
[email protected]_temperature.time =AliIoTClientJson.GetUnixTime();
// 低碳環境、節約資源,從你我做起,夏天空調不低於 26°
[email protected]_temperature.value=26.0F;
[email protected]_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();
// 設置屬性值
[email protected]_temperature.value = DeviceSimulate.Property(ref cpu_temperature, 40, 60, 8);
[email protected]_temperature.time = AliIoTClientJson.GetUnixTime();
// 低碳環境、節約資源,從你我做起,夏天空調不低於 26°
[email protected]_temperature.value = DeviceSimulate.Property(ref gree_temperature, 40, 60, 8); ;
[email protected]_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();
// 設置屬性值
[email protected]_temperature.value = DeviceSimulate.Property(ref cpu_temperature, 40, 60, 8);
[email protected]_temperature.time = AliIoTClientJson.GetUnixTime();
// 低碳環境、節約資源,從你我做起,夏天空調不低於 26°
[email protected]_temperature.value = DeviceSimulate.Property(ref gree_temperature, 40, 60, 8); ;
[email protected]_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進制弄不來的。 :joy: :joy: :joy:
有些同學非要用 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);
上傳屬性數據,可以分開上傳,不需要每次都要上傳全部的屬性。需要更新哪個屬性,就上傳這個屬性。