文章主题 

  在开发一个 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 的开发时间不长,以上分析过程难免会存在一些理解上的错误,希望没让您误入歧途。另外,知识的学习都是螺旋式的,不能追求一下子把所有相关的东西都理解正确,只要能满足当前的开发需求就可以了,循序渐进的提高、进步,最后就一定能够得到真经。


  1. 欢迎转载,请尊重版权,保留全部内容并注明来源。

  2. 如果这篇文章侵犯了您的权益,请在此处或微信公众号留言,我会及时处理。

  3. 如果需要一块探讨,请联系下面微信公众号。

ZWave对COMAND CLASS的处理流程的更多相关文章

  1. ENode 2.0 - 深入分析ENode的内部实现流程和关键地方的幂等设计

    前言 ENode是一个基于消息的架构,使用ENode开发的系统,每个环节都是处理消息,处理完后产生新的消息.本篇文章我想详细分析一下ENode框架内部是如何实现整个消息处理流程的.为了更好的理解我后面 ...

  2. shell中命令的执行流程

    在shell中,一个命令有3中写法: 1 可以直接写(Normal Command) 2 可以放在双引号中("Command") 3 可以放在单引号中('Comand') 这3中写 ...

  3. 深入分析ENode的内部实现流程和关键地方的幂等设计

    ENode 2.0 - 深入分析ENode的内部实现流程和关键地方的幂等设计 前言 ENode架构图 ENode框架内部实现流程分析 Command的幂等处理 Domain Event持久化时的并发冲 ...

  4. EDKII Build Process:EDKII项目源码的配置、编译流程[三]

    <EDKII Build Process:EDKII项目源码的配置.编译流程[3]>博文目录: 3. EDKII Build Process(EDKII项目源码的配置.编译流程) -> ...

  5. ZWave 中的消息队列机制

    文章主题   在我们的日常编程中,对消息队列的需求非常常见,使用一个简洁.高效的消息队列编程模型,对于代码逻辑的清晰性,对于事件处理的高效率来说,是非常重要的.这篇文章就来看看 ZWave 中是通过什 ...

  6. Linux下服务器端开发流程及相关工具介绍(C++)

    去年刚毕业来公司后,做为新人,发现很多东西都没有文档,各种工具和地址都是口口相传的,而且很多时候都是不知道有哪些工具可以使用,所以当时就想把自己接触到的这些东西记录下来,为后来者提供参考,相当于一个路 ...

  7. 基于netty http协议栈的轻量级流程控制组件的实现

    今儿个是冬至,所谓“冬大过年”,公司也应景五点钟就放大伙儿回家吃饺子喝羊肉汤了,而我本着极高的职业素养依然坚持留在公司(实则因为没饺子吃没羊肉汤喝,只能呆公司吃食堂……).趁着这一个多小时的时间,想跟 ...

  8. 通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的?

    在<中篇>中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的.总的来说,管道由一个服务器和一个HttpApplication构成 ...

  9. nginx+iis+redis+Task.MainForm构建分布式架构 之 (redis存储分布式共享的session及共享session运作流程)

    本次要分享的是利用windows+nginx+iis+redis+Task.MainForm组建分布式架构,上一篇分享文章制作是在windows上使用的nginx,一般正式发布的时候是在linux来配 ...

随机推荐

  1. Verilog代码和FPGA硬件的映射关系(三)

    组合逻辑和FPGA之间的映射关系我们知道了,那时序逻辑和FPGA之间又是一种怎样的映射关系呢?我们就以前面寄存器章节的例子来向大家说明,也一同把当时为什么用异步复位更节约资源的原因告诉大家.我们先来看 ...

  2. 这次终于可以愉快的进行 appium 自动化测试了

    appium 是进行 app 自动化测试非常成熟的一套框架.但是因为 appium 设计到的安装内容比较多,很多同学入门都跪在了环境安装的部分.本篇讲述 appium 安卓环境的搭建,希望让更多童鞋轻 ...

  3. css变量的使用

    css变量的使用 1.介绍:我们也可以在css中定义变量,和less.sass一样,通过--来定义变量 div { /* 开始定义变量 */ --color: red; /* 通过var()函数来使用 ...

  4. 剑指Offer之裴波那契数列

    题目描述 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1). n<=39 解法1:递归解法 public int Fibonacc ...

  5. 微软:正式发布针对 .NET Core的 Winform 设计器

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文出处:https://devblogs.microsoft.com/dotnet/windows-for ...

  6. webpack@next webpack-multi-page-cli 多页脚手架2.0

    根据自己的经验和想法,对原有的1.x版本进行的大版本的升级.在实际工作中,能结合的应用场景会更加多元化. github:https://github.com/pomelott/webpack-mult ...

  7. [PHP自动化-进阶]002.CURL模拟登录带有验证码的网站

    引言:继前文<模拟登录并采集数据>,大家似乎看不过瘾,这会再出一发,模拟实现带验证码网站的登录. 这篇文章主要介绍了PHP使用CURL实现对带有验证码的网站进行模拟登录的方法,可以帮助读者 ...

  8. html5学习之路_005

    PHP环境搭建 1.下载安装xampp 2.打开xampp,开启mysql和apache 3.在开发环境eclips中下载插件 4.安装php 5.切换到php开发环境 6.创建一个php项目 7.打 ...

  9. [Python基础]007.字符串

    字符串 内建操作 字符串长度 大小写变换 去空格或其他 连接字符串 查找替换 分割 判断 内建操作 字符串长度 len 代码 s = 'abcd' print len(s) 大小写变换 lower 小 ...

  10. Java和NodeJS解析XML对比

    Java解析XML 1.接收xml文件或者字符串,转为InputStream 2.使用DocumentBuilderFactory对象将InputStream转为document对象 Document ...