1. 程式人生 > >ZWave對COMAND CLASS的處理流程

ZWave對COMAND CLASS的處理流程

文章主題 

  在開發一個 ZWave Device 的過程中,對 COMAND CLASS(單詞太長了,後面就簡寫為 CC 啦) 的處理是最基本、最重要的工作。這篇文章以最最簡單的 CC:COMMNAD_CLASS_BASIC 為例子,來拆解、分析應用層對它的處理流程。

 

內容導航  

  • 接收指令

  • 處理指令

  • 傳送資料

       

接收指令  

1. 指令介面函式

  我們就以一個最簡單的場景為示例:閘道器 G 傳送指令:COMMNAD_CLASS_BASIC 給裝置 D。這個 CC 包含 3 個 COMMAND,分別是: BASIC_GET, BASIC_REPORT, BASIC_SET,官方提供的文件裡對這 3 個命令有詳細解釋。(這裡要說一句:官方的文件太 TMD 的多了,多的看不過來!)

先上圖1:a1.jpg

  是不是太亂了,有點接受不了啊?將就著看吧,我已經盡最大努力了,如果你會 PS,可以教教我。

  也就是上圖中標記0的地方,看起來是不是這些資料結構挺挺長、挺嚇人的?!其實 C 語言開發中,我覺得核心就是資料結構和演算法,其中資料結構又決定了演算法。在分析 ZWave 程式碼的過程中,有很大一部分就是分析資料結構,當然了這是我的觀點。

  圖中標記1是返回值的定義,它是一個列舉型別。

  圖中標記2是引數 rxOpt 的資料結構,它是一個結構體,這裡結構體裡面關於 NODE_ID 的解釋可以看一下原始碼,比較簡單。

  圖中標記3是引數 pCmd 的資料結構,這裡是重點:這是一個[共用體],簡單的說,就是記憶體中的一塊地址空間,你可以按照不同的組織方式去理解其中儲存的內容。

2. 關於 C 語言的共用體/共同體/union

  剛才的藐視似乎有點拗口,舉個栗子,比如:有一塊地址空間一共有 4 個位元組的容量:

  • 你可以定義一個指標 char *p, p 指向這個地址空間的首地址 A,然後你就可以讀取或者寫入一 char 型別的資料;然後移動指標 p 到第二個位元組的位置 A+1,讀取或者寫入另一 char 型別的資料;再然後是第三個位元組的地址 A+2;再然後是第四個位元組的地址 A+3。

  • 你也可以定義一個指標 int *p,指標 p 指向這個地址空間的首地址 A,此時,你可以直接讀取/寫入一個 4 位元組的 int 型資料。

    理解了上面的內容,那麼對 ZWave 指令部分的處理可以說就理解一半了。回到引數 pCmd,當裝置D 接收到不同的 CC 指令時,這個指標所指向的地址空間就儲存了不同的資料(什麼?這個地址空間是在哪裡分配的?我也不知道。可以肯定的是協議層為我們準備好的,應用開發者不用操心這個事情)。至於如何解析這些指令,當然是參考官方提供的文件,其中詳細說明了每一個 CC 所對應的指令中,每一個位元組,每一個 bit 代表什麼意思。

注意:在這個地址空間中,開頭2個位元組的意思一定是確定的,也就是圖1中標記5列出的:cmdClass 和 cmd。

    所以,在這個指令接收函式中,首先通過 pCmd->ZW_Common.cmdClass 來判斷該指令是哪一個 COMMAND CLASS。也就是說,pCmd 理解成:這個指標指向了 ZW_COMMON_FRAME 這個結構體。所以,我們就知道了當前的指令是:COMMAND_CLASS_BASIC,從而進入了第一個 case 中呼叫處理函式:handleCommandClassBasic() 

 

處理指令  

  handleCommandClassBasic( ) 函式位於 ApplicationHandlers 資料夾下面,這裡應該說是官方為了降低開發者的難度,把所有 CC 的處理邏輯的共性提取出來。

  繼續上圖:圖2。

a2.jpg

  好像比圖1更亂了?!真的是盡力了。

  這個函式首先判斷指令是 COMMAND_CLASS_BASIC 中的哪一個 COMMAND: BASIC_SET or BASIC_GET。

1. BASIC_SET

    看到沒?引數 pCmd 又指向了另一個數據結構,即圖中標記1處的 ZW_BASIC_SET_V2_FRAME。如果這裡沒有明白,需要回到上面再重新理解一下。

    首先檢查了引數 value 的範圍,BYTE 的型別是通過 typedef 定義的,本質上是 unsigned char。ZWave 對 Basic Value 允許的範圍是 0x00~0x63, 以及 0xFF,反正是代表不同的意思了。

然後呼叫函式 handleBasicSetCommand。 這個函式是什麼鬼?在哪裡? 原來這個函式是需要開發者自己實現的,官方已經替你在 CommandClassBasic.h 中聲明瞭這個函式是外部的,也就是需要開發者實現的。

    可以看到,傳遞了2個引數:

  • pCmd->ZW_BasicSetFrame.value

  • rxOpt->destNode.endpoint

    第一個引數是設定的 value,第二個引數是說這個value是用來設定哪一個 EndPoint 的,比如接收指令的裝置是一個有4個插孔的排插,那麼每一個插孔就是一個 EndPoint。當然,如果當裝置就是一個燈泡,那麼這個 EndPoint 就等於0。

2. BASIC_GET

從字面上理解:就是發出指令的閘道器G要從接收指令的裝置D獲取一些資訊,比如燈泡的亮度,那麼裝置D就要把當前的亮度值傳送給閘道器G。於是,這裡的處理流程是:

2.1 申請地址空間

申請的這塊地址空間賦值給 pTxBuf,待會需要傳送的資料就往這個地址放,圖中標記2的地方。先看一下檔案 ZW-tx_mutex.c 中一個重要的結構體變數:

static MUTEX myMutex; 

這個 MUTEX 結構體定義在圖中標記3處,也就是說,在系統的資料區域,預設定義了一塊記憶體地址,專門用於在傳送資料時使用。正因為這塊地址空間是共用的,所以每個時刻只能有一個人使用,所以使用互斥量來保護這塊地址空間。

簡言之:誰先搶到誰先用(上鎖),用完之後要歸還(解鎖)。

2.2 設定傳送者、接收者引數

RxToTxOptions(rxOpt, &pTxOptionsEx);這個函式的作用就是:設定接收資料的nodeId 好 endpoint等引數, 傳送者(也就是當前裝置自己)的 sourceEndpoint, 以及其他一些安全上屬性,都是從 rxOpt中複製而來。此時,裝置D變成了傳送者,閘道器G就程式設計了接受者。

    注意這個函式中的2個靜態變數:txOptionsEx, destNode。

2.3 往第一步得到的記憶體地址空間填資料

    至於需要填哪些資料,看文件!

    COMMAND_CLASS_BASIC 的 BASIC_GET 需要返回3個數據:

  • currentValue

  • targetValue

  • duration

    這3個數據都是由開發者定義的3個函式提供,至於這幾個值代表什麼意思,就需要開發者根據所開發的具體產品型別來決定了。拿燈泡做栗子,當前亮度是50,目標亮度是80,還需要5秒鐘達到目標亮度值。

2.4 傳送資料

    所有的資料傳送都是呼叫函式Transport_SendResponseEP,傳遞的幾個引數格式都是固定的,如果繼續跟進到這個函式裡,又是一個天地,特別是涉及到 MultiChannel 部分,也是比較複雜,以後再單獨拿出來分析。

    別忘了,傳送完成之後,呼叫了函式 FreeResponseBuffer,把申請的記憶體地址空間釋放掉,這裡並不是 free掉,而只是解鎖一下,對系統說:謝謝,我已經使用結束了,現在別人可以申請使用了。

 

總結  

  到這裡,COMMAND_CLASS_BASIC 的分析過程就結束了,其他的 COMMAND CLASS 執行流程是完全一樣的,有區別的地方就在於不同 CC 攜帶了不同的資料結構,當然,最開頭的2個位元組永遠是固定的:cmdClass 和 cmd。

請容忍我再囉嗦兩句啊。

  ZWave 的開發博大精深,文件更是數不勝數。我進入 ZWave 的開發時間不長,以上分析過程難免會存在一些理解上的錯誤,希望沒讓您誤入歧途。另外,知識的學習都是螺旋式的,不能追求一下子把所有相關的東西都理解正確,只要能滿足當前的開發需求就可以了,循序漸進的提高、進步,最後就一定能夠得到真經。

 


  1. 歡迎轉載,請尊重版權,保留全部內容並註明來源。

  2. 如果這篇文章侵犯了您的權益,請在此處或微信公眾號留言,我會及時處理。

  3. 如果需要一塊探討,請聯絡下面微信公眾號。

    IOT_Town_QR.png