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:
是不是太乱了,有点接受不了啊?将就着看吧,我已经尽最大努力了,如果你会 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。

好像比图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 的开发时间不长,以上分析过程难免会存在一些理解上的错误,希望没让您误入歧途。另外,知识的学习都是螺旋式的,不能追求一下子把所有相关的东西都理解正确,只要能满足当前的开发需求就可以了,循序渐进的提高、进步,最后就一定能够得到真经。
欢迎转载,请尊重版权,保留全部内容并注明来源。
如果这篇文章侵犯了您的权益,请在此处或微信公众号留言,我会及时处理。
如果需要一块探讨,请联系下面微信公众号。

ZWave对COMAND CLASS的处理流程的更多相关文章
- ENode 2.0 - 深入分析ENode的内部实现流程和关键地方的幂等设计
前言 ENode是一个基于消息的架构,使用ENode开发的系统,每个环节都是处理消息,处理完后产生新的消息.本篇文章我想详细分析一下ENode框架内部是如何实现整个消息处理流程的.为了更好的理解我后面 ...
- shell中命令的执行流程
在shell中,一个命令有3中写法: 1 可以直接写(Normal Command) 2 可以放在双引号中("Command") 3 可以放在单引号中('Comand') 这3中写 ...
- 深入分析ENode的内部实现流程和关键地方的幂等设计
ENode 2.0 - 深入分析ENode的内部实现流程和关键地方的幂等设计 前言 ENode架构图 ENode框架内部实现流程分析 Command的幂等处理 Domain Event持久化时的并发冲 ...
- EDKII Build Process:EDKII项目源码的配置、编译流程[三]
<EDKII Build Process:EDKII项目源码的配置.编译流程[3]>博文目录: 3. EDKII Build Process(EDKII项目源码的配置.编译流程) -> ...
- ZWave 中的消息队列机制
文章主题 在我们的日常编程中,对消息队列的需求非常常见,使用一个简洁.高效的消息队列编程模型,对于代码逻辑的清晰性,对于事件处理的高效率来说,是非常重要的.这篇文章就来看看 ZWave 中是通过什 ...
- Linux下服务器端开发流程及相关工具介绍(C++)
去年刚毕业来公司后,做为新人,发现很多东西都没有文档,各种工具和地址都是口口相传的,而且很多时候都是不知道有哪些工具可以使用,所以当时就想把自己接触到的这些东西记录下来,为后来者提供参考,相当于一个路 ...
- 基于netty http协议栈的轻量级流程控制组件的实现
今儿个是冬至,所谓“冬大过年”,公司也应景五点钟就放大伙儿回家吃饺子喝羊肉汤了,而我本着极高的职业素养依然坚持留在公司(实则因为没饺子吃没羊肉汤喝,只能呆公司吃食堂……).趁着这一个多小时的时间,想跟 ...
- 通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的?
在<中篇>中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的.总的来说,管道由一个服务器和一个HttpApplication构成 ...
- nginx+iis+redis+Task.MainForm构建分布式架构 之 (redis存储分布式共享的session及共享session运作流程)
本次要分享的是利用windows+nginx+iis+redis+Task.MainForm组建分布式架构,上一篇分享文章制作是在windows上使用的nginx,一般正式发布的时候是在linux来配 ...
随机推荐
- python 格式化输出(% VS format)
提到Python中的格式化输出方法,一般来说有以下两种方式: 1)% 格式说明由%和格式字符组成,如%f,%s,%d,它的作用是将数据按照指定的格式输出.格式说明是由“%”字符开始的. #1.输出字符 ...
- Robot Framework(2)- 快速安装
如果你还想从头学起Robot Framework,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1770899.html 安装RF cmd ...
- vue中生命周期
1,说器生命周期,总觉得有熟悉,又陌生,直到看到一道面试题,问父子组件的生命周期的执行顺序,我擦,真没太注意啊,不知道. 2,网上搜了一下,说法是有点像洋葱圈的形式,由外到内,在到外,因为就像一个盒子 ...
- iOS开发添加新手引导
往往项目中经常出现此类需求 用户通过点击引导按钮可响应页面附带按钮的点击事件. // // gzhGuideView.h // GuideView // // Created by 郭志贺 on 20 ...
- [JavaWeb基础] 021.Action中result的各种转发类型
在struts2中, struts.xml中result的类型有多种,它们类似于struts1中的forward,常用的类型有dispatcher(默认值).redirect.redirectActi ...
- leetcode198之打家劫舍问题
问题描述: 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警. 给 ...
- jQuery-键值对理解
1.form表单 <form action='url' method='get/post'> <input type='text'/> <input type='pass ...
- ES 或 Solr 分词器的相关面试题:
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 1.ES 中的 IK 分词器能分中文和英语的原因是什么? ik分词器为中文分词器,默认可对中文分词:通过 ...
- 使用turtle库绘制一个红色五角星图形
import turtle n = eval(input("请输入五角星的长度")) turtle.begin_fill() #开始填充颜色 i = 0 while i < ...
- Java实现 蓝桥杯 算法提高 小X的购物计划
试题 算法提高 小X的购物计划 问题描述 小X打算去超市shopping.小X没什么钱,只有N元.超市里有M种物品,每种物品都需要money,在小X心中有一个重要度.有的物品有无限件,有的物品只有几件 ...