文章目錄
- 概述
- 產品和設備
- 實例的產品和設備
- 產品和設備的關聯
- 單個產品有多個設備
- 為產品創建多個設備
- 產品模型和物模型
- 設備影子(遠程代理)
- 新建產品
- 模型定義
- 編解碼插件開發
- 編解碼插件工作原理
- 消息類型與二進制碼流
- 添加消息(數據上報消息)
- 添加消息(命令下發消息)
- 關聯消息和服務
- 插件部署
- 字段標記為地址域
- 字段的偏移值
- 標記為響應標識/mid
- 在線調試
- 新舊版本調試界面
- QA 設備發送成功,應用接收失敗
- 小結
概述
本文基于《HCIP-IoT Developer V2.5 實驗手冊》實驗1 —— 物聯網開發平臺實驗展開,按部就班其中的實驗過程,重點描述編解碼插件開發過程、概念理解、開發注意事項等,分析遇到的問題并提出解決方案,記錄并擴展基礎知識要點。HCIP-IOT物聯網開發平臺實驗:
本實驗通過在華為云物聯網平臺上創建產品,進行案例的功能定義和編解碼插件開發,掌握物聯網平臺的操作流程,以及如何驗證編解碼插件是否正確。本實驗完成后,你將掌握物聯網平臺的功能定義,掌握物聯網平臺編解碼插件的開發,掌握物聯網平臺的調試。
@History
剛開始我想硬著頭皮,依據《HCIP-IoT Developer V2.5 實驗手冊》按部就班,可當前實際IoTDA平臺頁面,有小部分與指導書中的描述并不匹配,或者指導書中的描述步驟過于跳躍,新手理解不了。在閱讀本文前,請先讀《IoT/HCIP實驗-1/物聯網開發平臺實驗Part1(快速入門,MQTT.fx對接IoTDA)》相關內容。 之后再接著來啃實驗手冊中的的內容。
產品和設備
如下圖,在華為IoTDA平臺中,其明確定義了:產品是設備的集合,是指某一類具有相同能力或特征的設備的合集。
有的地方將產品比作是模板,將設備比作實例,我覺著這種說法有些片面,但此時還不能具體說上來。然后又過了兩天…
@Note:本章節與《物聯網開發平臺實驗Part1》關聯比較大,是在其實驗基礎上的進一步的探究實驗和知識分析總結。
實例的產品和設備
如下圖,總覽是實例的總覽,其Web頁面顯示實例的基本信息,
如下圖,產品是實例的產品列表,
如下圖,設備是實例的設備列表,
也就是說,產品和實例在一定程度上是并列在IoTDA實例之下的。那么我們再來回顧下什么是實例?
在華為云物聯網平臺中,IoTDA實例 指的是云服務提供的虛擬化環境或資源單元,用于支持具體的應用與服務。根據不同的需求,華為云物聯網平臺提供了兩種實例類型:標準版(標準實例)和企業版(專享實例)。實例的含義非常類似于Docker的容器,具體可從如下三個方面來理解:
虛擬化資源:
實例是基于云計算技術虛擬化的資源單元,它可以包含計算、存儲和網絡等資源。每個實例可以運行特定的應用程序或服務,支持不同的業務場景。
隔離性:
實例提供了資源的隔離,確保不同用戶或不同應用之間的數據和資源不會互相干擾。這是云平臺的重要特性,能夠提升安全性和穩定性。
可擴展性:
實例可以根據用戶的需求進行擴展和縮減,以適應不同的工作負載。用戶可以靈活地選擇所需的資源配置,來提高系統的性能或降低成本。
產品和設備的關聯
關聯環節1-在設備注冊界面要指定其所屬的產品
關聯環節2-Topic主題消息的數據體中,含有產品模型的服務ID和屬性字段名稱,
通過上述分析我們可以推論出來的一個結論是,一個設備只能屬于一個產品。設備管理,是有分組功能的,但也不可使得一個設備服務于多個產品。
單個產品有多個設備
本小節我關注的終極問題是:一個產品下的多個設備,總不能強硬要求他們是相同的設備類型吧?
為產品創建多個設備
一個產品映射多個設備的情形在 <物聯網開發平臺實驗> 中并沒有被提及,這里我們實操驗證下。很給力的是,MQTT.fx 可以多進程實例啟動,這就方便多了。如下圖,我們在設備 <IoT路燈1> 的基礎上,新增設備路燈2 和 智慧電線桿100,哈哈,假設智慧電線桿上裝設有風速和濕度傳感器,并能上傳華為云IoTDA服務。
這里多提及一個事情,上述三個設備都是創建在同一個產品“智慧路燈-冶源”之下的,而該產品的協議類型是MQTT,數據格式是JSON,故所有產品繼承了協議類型是MQTT這一屬性,因此三個設備都可以使用MQTT.fx來進行模擬。通過使用Beyond Compare工具對比上述三個設備的MQTT參數可知,它們的MQTT Broker Profile Setting 是相同的,
三個MQTT.fx設備配置各自的MQTT參數后,均可以成功連接到IoTDA設備接入服務。我們在三個設備中都執行亮度數據上報,
在Web應用模擬器中查看,均可以正確接收到正確的數據上報結果。
產品模型和物模型
這里將從一個新的角度來審視產品模型的定義。定義產品模型的過程,實質上就是添加服務的過程,而每個服務都已包含一個屬性列表和一個命令列表。我們在物聯網開發平臺實驗Part1的基礎上,添加新服務 EnvData,代表電線桿上的環境傳感器,
給產品添加上述服務及其屬性字段后,我們重新查看設備詳細信息,可以看到該產品下的所有設備都具有了新添加的內容。也就是說在當前的IoTDA平臺下,即使某產品是由千差萬別的物理設備作用而成,在華為IoT管理平臺中,它們都要具有相同的產品模型。雖然每個設備具有全量的產品模型,但并不意味著每個設備都要上報全部種類的采集數據,這用腳指頭想也不可能。
怎么說呢?
前文是站在產品模型的角度來說的,我們再站到設備角度來看看,也許就明白了,還是以IoT路燈2為例,
如上圖所示,我們的IoT路燈2設備,完全可以只是/或者只能上報WindSpeed數據,而永遠不會上報Temperature數據。即一個具體的設備可以只是關聯部分產品模型服務或字段。
綜上所述,產品模型包含了所有設備的、綜合起來的、全部的數據上報功能和被控制功能,是一個徹頭徹尾的功能全集。而每個設備可以只是實現其中的一個或幾個功能,不同設備支持的功能類型可以是相同的,也可以是完全不同的。
設備影子(遠程代理)
影子設備的概念,看到過好幾次了,它是什么意思呢?我們以實驗Part1中設備<IoT路燈2>為例。
1、在設備詳情->設備信息選項卡->物模型數據->點擊查看全部屬性
2、在設備詳情->直接點擊設備影子
物聯網平臺支持創建設備的“影子”。設備影子是一個JSON文件,用于存儲設備的在線狀態、設備最近一次上報的設備屬性值、應用服務器期望下發的配置。每個設備有且只有一個設備影子,設備可以獲取和設置設備影子以此來同步設備屬性值,這個同步可以是影子同步給設備,也可以是設備同步給影子。
應用場景
適合資源受限低功耗設備,長期處于休眠狀態的場景。設備影子的具體作用如下,
1、查詢設備最新上報數據和設備最新在線狀態:
當在控制臺上查詢設備上報數據時,由于設備可能長時間處于離線狀態或因網絡不穩定掉線,而無法獲取到最新數據。通過設備影子機制,設備影子中始終保持設備最新上報的數據和設備當前狀態,控制臺上只需要查詢設備影子中存儲的數據,即可獲取設備最新上報的數據和設備狀態。
很多應用服務器頻繁地查詢設備在線狀態,由于設備處理能力有限,頻繁查詢會損耗設備性能。使用設備影子機制,設備只需要主動同步狀態給設備影子一次,多個應用程序請求設備影子獲取設備狀態,即可獲取設備最新狀態,從而將應用程序和設備解耦。
2、修改設備屬性值:
用戶通過“設備 > 設備詳情 > 設備影子”修改設備的屬性值。由于設備可能長時間處于離線狀態,修改設備屬性值的操作不能及時下發給設備。在這種情況下,物聯網平臺可以將修改設備的屬性信息存儲在設備影子中,待設備上線后,將修改的設備屬性值同步給設備,從而完成設備屬性值的修改。
按照我個人的理解,設備影子類似于是一種遠程代理架構或機制。設備影子充當了設備的狀態代理,實時反映設備的在線狀態和最近一次上報的屬性值。設備影子支持異步通信。設備影子機制實現了應用程序與設備之間的解耦。設備影子提供了一個集中管理設備狀態和屬性的方式。種設計理念不僅提升了物聯網系統的性能,還增強了用戶對設備管理的控制力。
新建產品
目前IoTDA服務所提供的創建產品的界面,與指導書中的截圖已經不太一樣,但也沒有什么不好理解的,順著填寫就行。
其中,
設備類型選擇,標準類型(包含所屬行業:智慧農業、所屬子行業:農業農機、設備類型:農業農機)
高級設置中,定義了產品ID為:dahequ_aa77bb,
產品創建后,
我們可以直接點擊上圖中的查看詳情,或者點擊確定后,在在產品列表中點擊產品名稱或操作中的詳情進入如下頁面,
模型定義
在實驗指導書(2019發布)中,模型定義卡是單獨占據一個Tab子界面的,現在模型定義配置界面在基本信息這個Tab之下,插件開發和在線調試還是單獨占據一個TabPage頁。按照指導書的描述,實驗是使用自定義模型。
如上圖,點擊"模型定義"配置卡中的"自定義模型"按鈕,立即就彈出來服務創建界面,按指導書完成后,模型定義卡顯示如下,
左側是服務類表(Agriculture是我們剛才添加的服務),每個服務包含一個屬性集合和一個命令集合。頁面格式也與指導書中描述有差異。接下來的,為Agriculture服務添加屬性和命令的操作很簡答,這里不再贅述,參考指導書即可。
最終定義好的模型如上圖。在指導書中,還有一種可行的訪問方式,在當前版本的平臺下實際沒有看到,可能是合并到可寫啦。
編解碼插件開發
編解碼插件工作原理
為了更好的理解編解碼插件的工作原理,我們從不同的文檔中獲取了其原理圖。
來自培訓手冊,
來自產品文檔,
來自圖形化開發界面的新手指引,
在一開始的學習過程中我并沒有注意到。在圖形化開發界面的右上角,有個新手指引連接,有詳細的圖形化開發過程。其中的工作原理說明部分是gif動態圖,比指導書詳細生動多了。
通過如上各個編解碼插件工作原理圖,我們對諸如,消息、服務、Profile、產品模型等概念的理解,將會逐漸清晰起來。
消息類型與二進制碼流
在編輯嗎插件的定義中,消息類型分為兩種,數據上報類型和命令下發類型。無論是那種消息類型都可指定響應。
我們以命令下發型消息 Agriculture_Control_Light 為例,分析該消息及其響應消息的字段和取值表,
如上,可能對應于如下C/C++結構數據定義,
typedef unsigned char u8_t; /* Unsigned 8 bit quantity */
typedef signed char s8_t; /* Signed 8 bit quantity */
typedef unsigned short u16_t; /* Unsigned 16 bit quantity */
typedef signed short s16_t; /* Signed 16 bit quantity */
typedef unsigned long u32_t; /* Unsigned 32 bit quantity */
typedef signed long s32_t; /* Signed 32 bit quantity */struct Agriculture_Control_Light /*report*/ {u8_t messageid; //==0x01u16_t mid; //0x0001s8_t Light[3]; //string# 4F4E==ON; 4F4646==OFF
}; //5bytesstruct Agriculture_Control_Light_ACK /*report*/ {u8_t messageid; //==0x02u16_t mid; //0x0001u8_t errcode; //0==OKu8_t Light_State; //0==ON; 1==OFF
}; //5bytes
添加消息(數據上報消息)
當前IoTDA平臺與指導書中的描述一致,據指導書按部就班即可。
如上圖,我們添加了一個名字為Agriculture的消息,這個消息有4個字段。這里我們把Agriculture理解為一個結構體就形象化多了。實際上前文將編解碼插件原理的時候也說過,所謂消息本質就是用于與設備端交互的二進制碼流。另外在實驗1中,我們并沒有定義編解碼插件,也能使得設備與平臺通信,因為彼時我們發送的是主題數據/MQTT格式的JSON數據,而不是二進制碼流,我們的設備使用的是MQTT協議不是CoAP等協議。
接下來,產品模型屬性與消息字段的綁定,后面的小節會繼續講解。
添加消息(命令下發消息)
按照指導書的步驟,完成“命令下發”類消息的構建,其中,Light命令消息,結果如下,
同理,我們添加Motor命令消息。在其添加過程中,我們遇到如下問題,
指導書中,Motor命令消息,其命令字段messageid0x1,響應字段messageid0x2。但在目前的平臺中進行操作時,其默認值分別是0x3和0x4,似乎是與Light命令消息進行排序的。為了驗證這個想法,我么再嘗試新加一個命令消息,
實驗結果符合預期,1-2-3-4-5-6,即消息類型同為“命令下發”的3對消息,其messageid是默認遞增編號的。
關聯消息和服務
如上兩節我們按照實驗手冊 <智慧農業案例插件設計思路> 中的消息列表及消息字段表,定義好全部的數據上報消息和命令下發消息和響應消息后,便可以使用IoTDA的圖形化開發模式,以拖拽的方式建立(設備)消息與平臺產品模型(服務)之間的關系。編解碼插件負責二進制碼流和JSON之間的數據格式轉換。
插件部署
這很簡單,但很重要,不要漏下啦哈。
另外與部署按鈕僅挨著的保存按鈕,也要時不時的去點擊下,以防止web頁意外關閉,導致丟失。
字段標記為地址域
messageID字段沒有與之綁定的屬性,因為它被標記為了地址域,那么什么是地址域呢?
如果你參與定義過軟件中的一些消息協議,這也很好理解,這里的地址域類似于我們常用的消息CTF(消息類別、消息類型、消息功能)標識(字段),該標識表明了接收方該如何解析后續數據字段或數據流塊。“標記為地址域” 是指將特定字段定義為消息類型的標識符,其字段名稱強制固定為messageId,其本質是消息協議中的元數據標識。
在華為 IoTDA 平臺的插件開發中,messageId 屬于平臺預定義的協議關鍵字,其命名權僅在標記地址域時開放,其他場景禁止使用。比如,若你把溫度字段錯誤命名為messageId,編解碼插件會誤將溫度值識別為消息類型標識,引發數據解析錯誤。其主要有兩層含義:
1、協議層定位
地址域是編解碼協議中用于區分消息類型的專用字段,通常作為二進制消息流的首個字段。例如:
0x00表示數據上報消息;0x01表示命令響應消息;0x02表示…
該字段不攜帶業務數據,僅用于標識后續數據的解析規則。
2、字段屬性強制約束
當某字段被標記為地址域時:
字段名稱必須為 messageId:系統強制約定,不可修改。
位置必須固定:在消息結構中的第一個字段位置。
數據類型限制:通常為整型或枚舉值,對應二進制碼流的解析規則。
結合前文的實驗過程,可看出來:
1、同為數據下發類型消息的全部消息定義及其響應消息的定義,其messageid是統一順序編號的,是不能重復的,在創建卡中也有輸入篩選過濾器存在,不允許你那么搞。
2、數據上報消息的messageid和命令下發類消息的messageid編號,沒有任何關系。這里,結合Topic定義和使用也很好理解,這兩種消息都是定義在系統Topic中的,它們的topic名中分別含有report和command字樣。
字段的偏移值
如果每個非地址域字段都是固定的數據類型值,則將上述插件定義中的消息及其字段與編程語言中的結構體概念映射起來,看起來沒有什么任何的違和感。但同時要注意上圖紫框中Tip提示,如果字段是可變長度的,則以實際偏移值為準。前文實驗中有使用string類型,但是都指定了字符字節長度。
標記為響應標識/mid
類型為"數據上報"的消息,沒有mid字段的定義。雖然數據上報類消息也支持設置響應字段,
如上圖,數據上報類型消息的響應字段是一個32bit的狀態碼,支持自定義。
通過實驗可知,在IoTDA下命令和命令的響應是兩種消息,它們的消息標識messageid通常分別為n和n+1,這不難理解。但是關于Mid,我剛開始沒有理解的地方是:命令下發消息及其響應消息中均要設置響應標識字段,mid字段到底是要干啥?
在華為云 IoTDA 的命令下發機制中,mid(Message Identifier)字段是消息的唯一追蹤標識符,其核心作用是實現命令請求與響應的精準匹配,并解決異步通信場景下的消息關聯難題。mid字段的核心作用,可從如下3點來理解,
- 命令與響應的逐一對應
場景:當平臺下發命令后,設備可能因網絡延遲或處理耗時無法立即響應,甚至可能因重試機制收到重復命令。
機制:平臺為每條命令生成全局唯一的mid,設備響應時必須攜帶相同的mid。mid可以很復雜,只要唯一就行,示例如下,
// 平臺下發命令(帶mid)
{"messageId": "SET_TEMPERATURE","mid": "20231108001","params": {"temp": 25}
}// 設備響應(返回相同mid)
{"messageId": "SET_TEMPERATURE_RESPONSE","mid": "20231108001","result": 0 // 0表示成功
}
- 防止重復處理與消息去重
問題:網絡抖動可能導致設備收到重復命令(如 MQTT QoS 1/2 機制的重發)。
解決方案:設備端通過mid判斷是否已處理過該命令,若發現重復mid則直接返回緩存響應,避免重復執行。 - 狀態追蹤與調試
運維價值:通過mid可快速在平臺日志中定位某條命令的全生命周期(下發、傳輸、響應、超時)。
在線調試
本章節將重點關注在測試過程中遇到的問題,歡迎留言問題一起討論和學習。
新舊版本調試界面
如上是平臺當下的新版的調試界面,通過右上角的“回到舊版”按鈕,可以切換到如指導書中描述的截圖模樣的調試界面。
QA 設備發送成功,應用接收失敗
現象:
在設備模擬中輸入 屬性內容 00193C0064,并執行發送,結果設備測顯示發送成功,但是應用測卻沒有收到信息。
分析
平臺消息追蹤信息顯示如下異常,
按照日志時間戳,從下到上的消息先后順序。
1、我們的編解碼器/codec并沒有找到。制造商識別碼xxx,應用標識xxx,設備標識xxx. 此錯誤表明 IoTDA 平臺在嘗試解析設備上報數據時,未找到與設備產品模型匹配的編解碼插件。
2、上行數據被IoTDA平臺正確接收
3、上報數據中的服務ID,是為被定義的…
我們重新審視下 00193C0064這個二進制數據。在實驗指導書中,它代表的是數據上報消息,
typedef unsigned char u8_t; /* Unsigned 8 bit quantity */
typedef signed char s8_t; /* Signed 8 bit quantity */
typedef unsigned short u16_t; /* Unsigned 16 bit quantity */
typedef signed short s16_t; /* Signed 16 bit quantity */
typedef unsigned long u32_t; /* Unsigned 32 bit quantity */
typedef signed long s32_t; /* Signed 32 bit quantity */struct Agriculture /*report*/ {u8_t messageid;u8_t Temperature;u8_t Humidity;u16_t Luminance;
}; //5bytes
期望的結果是 { Temperature=25,Humidity=60,Luminance=100 },多字節以大端字節序計。關于字節序的問題,可以參考《存儲與傳輸/大小端字節序的概念、決定因素、給編程帶來的困擾》等文章。
按照大端字節序,即人類對字節的閱讀順序,上述結構體的對象展開為,
messageid =0x00,Temperature=0x19,Humidity=0x3C,Luminance=0x0064
有了上述分析,再結合平臺消息追蹤提示+編解碼插件動態工作原理展示,我去排查了我的Agriculture服務定義中的屬性列表,
如上,服務屬性中,溫度和濕度字段的數據類型int(整型)與對應消息字段的數據類型int8u并不一致。我本來很肯定這就是問題所在了,結果很快打臉。因為在編解碼插件服務屬性配置中,壓根就沒有int8u這個數據類型,關于整其只有int和long類型。我沒有理解這個事情,但是我確定,IoTDA處理了這種差異,是允許上述數據類型不嚴格匹配的。
原因確定
哈哈,我又要抽自己了。我再定義完成全部消息定義時,就著急忙活的來進行調試了。卻落下了至關重要的一個步驟,部署插件。
解決方案
在插件編輯卡的右上角,點擊部署后,重新測試,
(舊版調試界面)效果如下,
(新版調試界面)效果如下,
這里有個事情要注意下,可能是網絡延遲的關系。一小段時間(1min左右)內,我在服務配置、消息配置、部署操作完全正確的情況下,模擬進行設備數據的上報操作時,在調試打印串口,顯示的設備上報成功,應用模擬器窗口卻沒有顯示收到的json數據,但是過會,再次進行嘗試時,問題就不存在了。
小結
@Note 關于指導書的使用
現在我們完成了實驗指導書中的實驗1,我們發現,指導書中的小部分描述和截圖,確實已經不再完全切合當前IoTDA的功能或功能展示界面。我們要接受和容忍這種差異性,不要因此影響學習心情。以本實驗為例,其實我們重點關注智慧農業案例插件設計思路即可,包含編解碼插件工作原理、消息定義列表,具體消息字段定義列表等。