HomeAutomation流程分析
基于Zstack-CC2530-2.3.0-1.4.0中HomeAutomation例程
在分析整个应用之前,我们首先需要了解每个任务大概的处理流程。按下light板(Enddevice)的右键,请求终端banding,此时按下switch板(协调器)的右键,终端banding建立。按下switch板的上键可以切换light板上LED的亮灭。通过这个,我们来了解整个过程的实现。
终端banding暂且不说,先看任务的初始化及处理。
Light板:
(1)任务初始化:
void zclSampleLight_Init( byte task_id )
{
zclSampleLight_TaskID = task_id;//初始化任务ID
// Set destination address to indirect
//zclSampleLight_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
//zclSampleLight_DstAddr.endPoint = 0;
//zclSampleLight_DstAddr.addr.shortAddr = 0;
// This app is part of the Home Automation Profile
//AF层简单描述符的注册
zclHA_Init( &zclSampleLight_SimpleDesc );
// Register the ZCL General Cluster Library callback functions
//对回调函数的注册,ZCL将处理相应的事件
zclGeneral_RegisterCmdCallbacks( SAMPLELIGHT_ENDPOINT,
&zclSampleLight_CmdCallbacks );
// Register the application's attribute list
//属性列表的注册
zcl_registerAttrList( SAMPLELIGHT_ENDPOINT,
SAMPLELIGHT_MAX_ATTRIBUTES, zclSampleLight_Attrs );
// Register the Application to receive the unprocessed Foundation command/response messages
//从后面可以看出除了上面那些特定事件外的其他事件,交由zclSampleLight来处理
zcl_registerForMsg( zclSampleLight_TaskID );
……
}
void zcl_Init( uint8 task_id )
{
zcl_TaskID = task_id;
plugins = (zclLibPlugin_t *)NULL;//相应CLUSTERID及回调函数列表
attrList = (zclAttrRecsList *)NULL;//相应事件属性列表
clusterOptionList = (zclClusterOptionList *)NULL;
}
(2)任务处理:
uint16 zcl_event_loop( uint8 task_id, uint16 events )
{
uint8 *msgPtr;
(void)task_id; // Intentionally unreferenced parameter
if ( events & SYS_EVENT_MSG )
{
msgPtr = osal_msg_receive( zcl_TaskID );
while ( msgPtr != NULL )
{
uint8 dealloc = TRUE;
if ( *msgPtr == AF_INCOMING_MSG_CMD )
{
zclProcessMessageMSG( (afIncomingMSGPacket_t *)msgPtr );
}
else if ( zcl_RegisteredMsgTaskID != TASK_NO_TASK )
{
//这些事件由zclSampleLight_event_loop来处理
// send it to another task to process.
osal_msg_send( zcl_RegisteredMsgTaskID, msgPtr );
dealloc = FALSE;
}
……
}
……
}
// Discard unknown events
return 0;
}
下面我们来看一下,zclProcessMessageMSG中是如何来处理这些事件的。
void zclProcessMessageMSG( afIncomingMSGPacket_t *pkt )
{
endPointDesc_t *epDesc;
zclIncoming_t inMsg;
zclLibPlugin_t *pInPlugin;
zclDefaultRspCmd_t defautlRspCmd;
uint8 options;
uint8 securityEnable;
uint8 interPanMsg;
ZStatus_t status = ZFailure;
if ( pkt->cmd.DataLength == 0 )
return; // Error, ignore the message
// Initialize
inMsg.msg = pkt;
inMsg.attrCmd = NULL;
inMsg.pData = NULL;
inMsg.pDataLen = 0;
inMsg.pData = zclParseHdr( &(inMsg.hdr), pkt->cmd.Data );
inMsg.pDataLen = pkt->cmd.DataLength;
inMsg.pDataLen -= (uint16)(inMsg.pData - pkt->cmd.Data);
// Find the wanted endpoint
//寻找我们想要的endpoint(前面初始化的时候,我们有注册)
epDesc = afFindEndPointDesc( pkt->endPoint );
if ( epDesc == NULL )
return; // Error, ignore the message
if ( pkt->clusterId == ZCL_INVALID_CLUSTER_ID )
return; // Error, ignore the message
if ((epDesc->simpleDesc == NULL) || (zcl_DeviceOperational(pkt->endPoint,
pkt->clusterId, inMsg.hdr.fc.type, inMsg.hdr.commandID,
epDesc->simpleDesc->AppProfId) == FALSE))
{
return; // Error, ignore the message
}
#if defined ( INTER_PAN )
if ( StubAPS_InterPan( pkt->srcAddr.panId, pkt->srcAddr.endPoint ) )
{
// No foundation command is supported thru Inter-PAN communication
if ( zcl_ProfileCmd( inMsg.hdr.fc.type ) )
return;
interPanMsg = TRUE;
options = AF_TX_OPTIONS_NONE;
}
else
#endif
{
interPanMsg = FALSE;
options = zclGetClusterOption( pkt->endPoint, pkt->clusterId );
}
// Local and remote Security options must match except for Default Response command
//inMsg.hdr.fc.type= ZCL_FRAME_TYPE_SPECIFIC_CMD
//就是处理加密与否,但是还没有弄明白clusterOptionList在哪里处理的,所以不知道options = zclGetClusterOption( pkt->endPoint, pkt->clusterId );的结果,暂时跳过吧,~~~~(>_<)~~~~
if ( !zcl_DefaultRspCmd( inMsg.hdr ) )//为真
{
securityEnable = ( options & AF_EN_SECURITY ) ? TRUE : FALSE;
if ( pkt->SecurityUse != securityEnable )
{
if ( UNICAST_MSG( inMsg.msg ) )
{
// Send a Default Response command back with no Application Link Key security
if ( securityEnable )
zclSetSecurityOption( pkt->endPoint, pkt->clusterId, FALSE );
defautlRspCmd.statusCode = status;
defautlRspCmd.commandID = inMsg.hdr.commandID;
zcl_SendDefaultRspCmd( inMsg.msg->endPoint, &(inMsg.msg->srcAddr),
inMsg.msg->clusterId, &defautlRspCmd,
ZCL_FRAME_SERVER_CLIENT_DIR, true,
inMsg.hdr.manuCode, inMsg.hdr.transSeqNum );
if ( securityEnable )
zclSetSecurityOption( pkt->endPoint, pkt->clusterId, TRUE );
}
return; // Error, ignore the message
}
}
// Is this a foundation type message
if ( zcl_ProfileCmd( inMsg.hdr.fc.type ) )//为假
{
if ( inMsg.hdr.fc.manuSpecific )
{
// We don't support any manufacturer specific command
status = ZCL_STATUS_UNSUP_MANU_GENERAL_COMMAND;
}
else if ( ( inMsg.hdr.commandID <= ZCL_CMD_MAX ) &&
( zclCmdTable[inMsg.hdr.commandID].pfnParseInProfile != NULL ) )
{
zclParseCmd_t parseCmd;
parseCmd.endpoint = pkt->endPoint;
parseCmd.dataLen = inMsg.pDataLen;
parseCmd.pData = inMsg.pData;
// Parse the command, remember that the return value is a pointer to allocated memory
inMsg.attrCmd = zclParseCmd( inMsg.hdr.commandID, &parseCmd );
if ( (inMsg.attrCmd != NULL) && (zclCmdTable[inMsg.hdr.commandID].pfnProcessInProfile != NULL) )
{
// Process the command
if ( zclProcessCmd( inMsg.hdr.commandID, &inMsg ) == FALSE )
{
// Couldn't find attribute in the table.
}
}
// Free the buffer
if ( inMsg.attrCmd )
osal_mem_free( inMsg.attrCmd );
if ( CMD_HAS_RSP( inMsg.hdr.commandID ) )
return; // We're done
status = ZSuccess;
}
else
{
// Unsupported message
status = ZCL_STATUS_UNSUP_GENERAL_COMMAND;
}
}
else // Not a foundation type message, so it must be specific to the cluster ID.
//inMsg.hdr.fc.type= ZCL_FRAME_TYPE_SPECIFIC_CMD
{
if (epDesc->simpleDesc == NULL)
{
pInPlugin = NULL;
}
else
{
// Find the appropriate plugin
pInPlugin = zclFindPlugin( pkt->clusterId, epDesc->simpleDesc->AppProfId );
}
if ( pInPlugin && pInPlugin->pfnIncomingHdlr )
{
// The return value of the plugin function will be
// ZSuccess - Supported and need default response
// ZFailure - Unsupported
// ZCL_STATUS_CMD_HAS_RSP - Supported and do not need default rsp
// ZCL_STATUS_INVALID_FIELD - Supported, but the incoming msg is wrong formatted
// ZCL_STATUS_INVALID_VALUE - Supported, but the request not achievable by the h/w
// ZCL_STATUS_SOFTWARE_FAILURE - Supported but ZStack memory allocation fails
//此处就是前面注册的回调函数,将对事件进行相应的处理
status = pInPlugin->pfnIncomingHdlr( &inMsg );
if ( status == ZCL_STATUS_CMD_HAS_RSP || ( interPanMsg && status == ZSuccess ) )
return; // We're done
}
……
}
还记得前面zclSampleLight_Init中,有调用zclGeneral_RegisterCmdCallbacks( SAMPLELIGHT_ENDPOINT,
&zclSampleLight_CmdCallbacks );函数,对端点SAMPLELIGHT_ENDPOINT进行注册,其数据结构如下:
typedef struct zclGenCBRec
{
struct zclGenCBRec *next;
uint8 endpoint; // Used to link it into the endpoint descriptor
zclGeneral_AppCallbacks_t *CBs; // Pointer to Callback function
} zclGenCBRec_t;
CB就是回调函数列表,其赋值列表如下。在上面的处理函数中,通过调用pInPlugin->pfnIncomingHdlr( &inMsg );调用,对事件进行相应处理。
static zclGeneral_AppCallbacks_t zclSampleLight_CmdCallbacks =
{
zclSampleLight_BasicResetCB, // Basic Cluster Reset command
zclSampleLight_IdentifyCB, // Identify command
zclSampleLight_IdentifyQueryRspCB, // Identify Query Response command
zclSampleLight_OnOffCB, // On/Off cluster command
……
};
对整个switchàlight控制,light板在APP层的流程基本如上。Switch板就略过了,基本上就是按下向上按键(HAL_KEY_SW_1)后,zclSampleSw_event_loop调用zclSampleSw_HandleKeys进行处理,调用zclGeneral_SendOnOff_CmdToggle,最终通过AF_DataRequest函数,将命令发送出去。