查看: 9539|回复: 10

关于协议栈中绑定流程的一点认识(三)

[复制链接]
shaomengchao 发表于 2010-12-8 17:45:16 | 显示全部楼层 |阅读模式
终于要说到End_Device_Bind_rep绑定服务的核心了,关于这点我之前一直很纠结。幸好看了小峰的blog以及ZIGBEE SPECIFICATION的描述才勉强理解。在分析ZDMatchSendState函数之前,我先贴一段小峰blog中关于End_Device_Bind_rep服务的描述。

在《Z-Stack 开发指南》中对End_Device_Bind_req的处理流程是这么描述的:
------------------------------------
  当协调器接收到第一个绑定请求时,他会在一定的时限内保留这一请求并等待第二个请求的出现。(默认的最长时间间隔是16秒)。
  一旦协调器接收到两个需要匹配的终端设备绑定请求时,它就会启动绑定过程,为发出请求的设备建立源绑定条目。假设在ZDO终端设备绑定请求中找到匹配,协调器将采取以下步骤:
1. 协调器发送一个ZDO解除绑定请求给第一个设备。终端设备绑定  是一个切换过程,所以解除绑定请求需要发送给第一个设备,以便移除一个已有的绑定条目。
2. 等待ZDO解除绑定的应答,如果返回的状态是ZDP_NO_ENTRY(没有已存在的绑定),协调器可以发送一个ZDO绑定请求,在源设备(ZDP_EndDeviceBindReq()第一个参数指定的地址)中建立绑定条目。假如此时返回的状态是ZDP_SUCCESS,可继续处理第一个设备的簇标识符(解除绑定指令已经移除了绑定条目,即已经切换完成)。
3. 等待ZDO绑定应答。收到以后,继续处理第一个设备的下一个簇标识符。
4. 等第一个设备完成了以后,在第二个设备上实行同样的过程。
5. 等第二个设备也完成了,协调器向两个设备发送ZDO终端设备绑定应答消息。
注意打开编译选项:REFLECTOR和ZDO_COORDINATOR

   ZDMatchSendState要做的就是要做的就是上面提出的5点了,由于这个函数调用次数很多而且每次调用功能都有些不同就恕我不重复贴出来了。我下面就简单分析下函数的执行流程。
   第一次调用ZDMatchSendState( uint8 reason, uint8 status, uint8 TransSeq )   ,其中reason=ZDMATCH_REASON_START,status=ZDP_SUCCESS, TransSeq=0(为什么是0?因为这次调用我们用不到这个参数)
   在这次调用中ZDMatchSendState就是为了发出一个Unbind_req请求。至于为什么要先要解除申请节点的绑定,我认为可能该节点在其他某个时间段已经完成了一次节点绑定(毕竟节点绑定的方法不止End_Device_Bind_req一种),所以一旦发现绑定申请节点已经获得了节点绑定就先解除它。而发送Unbind_req的帧格式如下:
SourceAddr(存储绑定表的节点的ieee地址) + SrcEPIntf (存储绑定表的节点的匹配的endpoint)+ ClusterID(匹配簇号) +  addrMode(要被绑定节点的地址格式)+destinationAddr(要被绑定节点的地址).这些信息都是从matched中获取的。
  至于Unbind_req的接受在存储绑定表节点的ZDApp_ProcessMsgCBs里,而REFLECTOR的编译选项是要选的。在注册函数ZDApp_RegisterCBs里
#if defined ( REFLECTOR )
  ZDO_RegisterForZDOMsg( ZDAppTaskID, Bind_req );
  ZDO_RegisterForZDOMsg( ZDAppTaskID, Unbind_req );
#endif
以及在ZDApp_ProcessMsgCBs里
#if defined ( REFLECTOR )
    case Bind_req:
    case Unbind_req:
      {
        ZDO_BindUnbindReq_t bindReq;
        ZDO_ParseBindUnbindReq( inMsg, &bindReq );
        ZDO_ProcessBindUnbindReq( inMsg, &bindReq );
      }
      break;
#endif
这个调用的流程大家自己研究osal的机制,我就不多说了。反正最后节点调用了ZDO_ProcessBindUnbindReq去处理coordinator的Unbind_req请求的。细心的同学可能发现了这个服务的处理同时也是兼容Bind_req请求的,当然这个是后话了,我们之后再提。
关于ZDO_ProcessBindUnbindReq函数前半部分是提取消息信息以及地址确认,之后关于clusterID有个分支,我们这里进入的是Unbind_req部分
else // Unbind_req
      {
        if ( APSME_UnBindRequest( pReq->srcEndpoint, pReq->clusterID, //关键函数,参数分别是之前Unbind_req帧中的
                       &(pReq->dstAddress), pReq->dstEndpoint ) == ZSuccess ) //绑定表节点的endpoint号、匹配簇号、被绑定节点地址
        {                                                                                                              //被绑定节点的endpoint号,由这些参数我们就可以执行   
          bindStat = ZDP_SUCCESS;                                                                 //解除绑定服务,这里我们假设绑定表节点之前没有绑定过
                                                                                                                    //其他节点,当然函数返回值不会是ZSuccess了
          // Notify to save info into NV
          ZDApp_NVUpdate();
        }
        else
          bindStat = ZDP_NO_ENTRY;
      }
//发送Unbind_rsq帧给coordinator,内容只有一个字节就是 bindStat = ZDP_NO_ENTRY
ZDP_SendData( &(inMsg->TransSeq), &(inMsg->srcAddr), (inMsg->clusterID | ZDO_RESPONSE_BIT), 1, &bindStat,inMsg->SecurityUse );

于是我们再次回到coordinator节点,关于Unbind_rsq服务的函数也是在ZDApp_ProcessMsgCBs,不过这个是coordinator的ZDApp_ProcessMsgCBs
#if ( ZG_BUILD_COORDINATOR_TYPE )
    case Bind_rsp:
    case Unbind_rsp:
      if (ZG_DEVICE_COORDINATOR_TYPE && matchED)
      {
        ZDMatchSendState(
             (uint8)((inMsg->clusterID == Bind_rsp) ? ZDMATCH_REASON_BIND_RSP : ZDMATCH_REASON_UNBIND_RSP),
             ZDO_ParseBindRsp(inMsg), inMsg->TransSeq );
      }
      break;
看到了 ZDMatchSendState了吧,是不是很眼熟,当然后面我们会越来越熟的。
第二次调用ZDMatchSendState,参数分别为ZDMATCH_REASON_UNBIND_RSP、ZDP_NO_ENTRY和inMsg->TransSeq
在这次调用中ZDMatchSendState函数干的事和上次几乎一样,唯一区别就是把上次发送的Unbind_req改成了bind_req,其他的就连封装的帧内容都是一样的。

好的,我们又到了绑定表节点,不同的这次人家收到的是bind_req请求。不过可惜ZDApp_ProcessMsgCBs一样处理,最后还是调用了ZDO_ProcessBindUnbindReq。
这次我们在ZDO_ProcessBindUnbindReq的分支选项不是Unbind_req,于是内容也相应变成了
if ( inMsg->clusterID == Bind_req )
      {
        // Assume the table is full
        bindStat = ZDP_TABLE_FULL;

        if ( bindNumOfEntries() < gNWK_MAX_BINDING_ENTRIES ) //bind没满还塞的下
        {
          if ( APSME_BindRequest( pReq->srcEndpoint, pReq->clusterID,   //上次是解除绑定这次是把绑定绑上了,于是在绑定表节点中绑定服务完成
                         &(pReq->dstAddress), pReq->dstEndpoint ) == ZSuccess )
          {
            uint16 nwkAddr;

            // valid entry
            bindStat = ZDP_SUCCESS;

            // Notify to save info into NV
            ZDApp_NVUpdate();

            // Check for the destination address
            if ( pReq->dstAddress.addrMode == Addr64Bit )//获得nwk地址
            {
              if ( APSME_LookupNwkAddr( pReq->dstAddress.addr.extAddr, &nwkAddr ) == FALSE )  
              {//找不到对应nwk地址的话就广播出NwkAddrReq请求获得
                ZDP_NwkAddrReq( pReq->dstAddress.addr.extAddr, ZDP_ADDR_REQTYPE_SINGLE, 0, 0 );
              }
            }
          }
        }
这样看来节点的一个绑定已经完成了,但coordinator还不知道呢。接下来 调用 ZDP_SendData利用Bind_rsp把这个好消息告诉它。


就这样我们有一次的回到了coordinator节点,又是ZDApp_ProcessMsgCBs中的 ZDMatchSendState,已经是第3次了。
本以为这次 ZDMatchSendState获得Bind_rsp成功后可以安然归西了吧。但它还没完
if ( reason != ZDMATCH_REASON_START && matchED->sending == ZDMATCH_SENDING_UNBIND )
  {
    // Move to the next cluster ID
    if ( matchED->ed1numMatched )
      matchED->ed1numMatched--;
    else if ( matchED->ed2numMatched )
      matchED->ed2numMatched--;
  }
也就是说一旦匹配簇不止一个的话它还会重复上面的过程。换成了一个匹配簇会,它又发一个Unbind_req继续我们之前蛋疼的过程。
我是受够了,我假设只有一个匹配簇,这样我们可以顺利的终止它。
在这种情况下 ZDMatchSendState会调用
dstAddr.addr.shortAddr = matchED->ed1.srcAddr;
    ZDP_EndDeviceBindRsp( matchED->ed1.TransSeq, &dstAddr, rspStatus, matchED->ed1.SecurityUse );

    // send response to second requester
    if ( matchED->state == ZDMATCH_SENDING_BINDS )
    {
      dstAddr.addr.shortAddr = matchED->ed2.srcAddr;
      ZDP_EndDeviceBindRsp( matchED->ed2.TransSeq, &dstAddr, rspStatus, matchED->ed2.SecurityUse );
    }

    // Process ended - release memory used
    ZDO_RemoveMatchMemory();
也就是说coordinator给绑定节点一个End_Device_Bind_rsp请求,最后清除了marched的内容就结束了它End_Device_Bind_req服务之旅。

而绑定节点收到了End_Device_Bind_rsp这个帧,在GenericApp_ProcessZDOMsgs中
case End_Device_Bind_rsp:
      if ( ZDO_ParseBindRsp( inMsg ) == ZSuccess )
      {
        // Light LED
        HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );
      }
#if defined(BLINK_LEDS)
      else
      {
        // Flash LED to show failure
        HalLedSet ( HAL_LED_4, HAL_LED_MODE_FLASH );
      }
#endif
      break;
也就是是亮个灯意思一下给我们看:你看我们绑定好了。

在这关于End_Device_Bind_req的绑定服务我就说完了,这个比Match_Desc_req复杂多了。当然还是有其他绑定方法的,比如说利用zb_BindDevice之类的,方法和End_Device_Bind_req还是有一定的区别的,这个我们以后再说吧。
葫芦兄弟 发表于 2010-12-8 21:10:58 | 显示全部楼层
楼主辛苦了
wuxiujiang 发表于 2010-12-9 11:52:59 | 显示全部楼层
  来顶下,顺便偷学了!  
xiaoyige 发表于 2011-1-27 16:41:26 | 显示全部楼层
顶顶顶顶顶顶
NangelQ 发表于 2011-5-27 09:46:40 | 显示全部楼层
谢谢楼主,对绑定已有比较详细的理解了~~~
ylxren 发表于 2012-7-31 22:43:08 | 显示全部楼层
学习{:soso_e100:}
lizheng86312 发表于 2012-8-29 12:04:20 | 显示全部楼层
学到东西了。顶。。。
ccc_ccccc 发表于 2013-3-16 14:26:42 | 显示全部楼层
好难啊 啊啊啊啊啊啊啊啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
啊啊啊 啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊 啊
tlk214 发表于 2013-8-12 14:34:03 | 显示全部楼层
绑定流程学习(三),楼主万岁!
hsamdy 发表于 2014-8-11 15:37:58 | 显示全部楼层
多谢楼主的分享,收获很多!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表