/ /** \param pSdoInfoInd 指向接收邮箱的数据,数据从主站读到的. \return 指示,如果一个当操作的时候,一个错误发生 ( good = 0 ). \brief 这个函数被调用当一个SDO-Info请求服务从主站上面接收到和根据相关函数的Opcode调用相对应的函数 * UINT8 SDOS_SdoInfoInd( TSDOINFORMATION MBXMEM *pSdoInfoInd ) { UINT8 abort = 0; /* 这个变量opCode包含请求的SDO 信息的类型 */ UINT8 opCode = (UINT8) ((pSdoInfoInd->SdoHeader.InfoHead & INFOHEAD_OPCODE_MASK) >> INFOHEAD_OPCODE_SHIFT); OBJCONST TOBJECT OBJMEM * pObjEntry; UINT16 index; UINT8 flags = COE_SERVICE; /* 它需要被检查,如果邮箱协议是正确的,发送邮箱数据长度需要长于需要的SDO服务类型的服务的数据帧头 */ if ( opCode == SDOINFOSERVICE_ENTRYDESCRIPTION_Q ) { if ( pSdoInfoInd->MbxHeader.Length < SIZEOF_SDOINFOENTRYREQSTRUCT ) return MBXERR_SIZETOOSHORT; } else { if ( pSdoInfoInd->MbxHeader.Length < SIZEOF_SDOINFOLISTSTRUCT ) return MBXERR_SIZETOOSHORT; } switch ( opCode ) { case SDOINFOSERVICE_OBJDICTIONARYLIST_Q://Object list request /* 一个对象字典集被需要,检查是否这个类型集是否被支持 */ if ( SWAPWORD(pSdoInfoInd->SdoHeader.Data.List.ListType) <= INFO_LIST_TYPE_MAX ) { UINT16 size = 0; /* 变量listType包含着需要的listType */ UINT8 listType = (UINT8) SWAPWORD(pSdoInfoInd->SdoHeader.Data.List.ListType); /* SDO信息数据帧头需要被存储,因为这个函数将会被再次调用,如果响应不能再一个邮箱服务里面被发送 变量nSdoInfoFragmentsLeft是等于0在第一次调用的时候和不等于0在接下来的调用的时候 */ MBXMEMCPY(aSdoInfoHeader, pSdoInfoInd, SDO_INFO_HEADER_BYTE_SIZE); if ( listType-- == 0 )//如果listType是等于0:返回只是list的长度//0x00:Object number requested on 5different lists { /* List-Type 0: 字典列表的长度 */ UINT8 i; /* 给List-Type0响应需要的邮箱的长度只是24个字节,这个邮箱经常小于24个字节来支持SDO的信息服务 */ nSdoInfoFragmentsLeft = 0; for (i = 0; i < INFO_LIST_TYPE_MAX; i++) { UINT16 n = OBJ_GetNoOfObjects(i); /* 拷贝列表当中对象字典的数量到SDO信息响应当中 */ ((UINT16 MBXMEM *) &pSdoInfoInd->CoeHeader)[(SIZEOF_SDOINFOLISTSTRUCT>>1)+i] = SWAPWORD(n); } /* 邮箱响应服务的长度 */ size = (INFO_LIST_TYPE_MAX << 1) + SIZEOF_SDOINFOLISTSTRUCT; } else { /* 需要的对象字典列表能返回有索引和长度的 */ UINT16 MBXMEM * pData; UINT16 n = 0; if ( nSdoInfoFragmentsLeft ) { /* 下一个碎片有SDO信息影响的应该被发送 */ /* 用最长的长度来初始化长度用它来适应邮箱的服务 */ { size = u16SendMbxSize - SIZEOF_SDOINFO - MBX_HEADER_SIZE; } /* 用指针初始化pData 其中fragment应该需要被拷贝 */ pData = &((UINT16 MBXMEM *) &pSdoInfoInd->CoeHeader)[SIZEOF_SDOINFO>>1]; /* 用下一个索引来初始化索引用来被发送 */ index = nSdoInfoIndex; /* 减少被发送数据包的number */ nSdoInfoFragmentsLeft--; } else { /* SDO响应信息的第一个数据包应该被发送 */ /* 获得需要的对象字典列表中对象的数量 */ n = OBJ_GetNoOfObjects(listType); /* 我们从0x1000中开始 */ index = 0x1000; /* 用适合一个邮箱服务的最大长度来初始化长度 */ { size = u16SendMbxSize - SIZEOF_SDOINFOLISTSTRUCT - MBX_HEADER_SIZE; } /* 初始化pData指针,指向数据包,它应该被复制 */ pData = &((UINT16 MBXMEM *) &pSdoInfoInd->CoeHeader)[SIZEOF_SDOINFOLISTSTRUCT>>1]; /*ECATCHANGE_START(V5.01) SDO4*/ /*检查分段里面是否有列表需要被发送*/ if( (n<<1) > size) { /*如果字节的数量被传输的不适合一个邮箱数据包的长度*/ /*计算数据包分包的个数,它需要被发送所有的字节数 - 字节它将会以当前的响应被传输,它是由following fragments这一位被划分的 */ nSdoInfoFragmentsLeft = (((n<<1)-size) /(size + 2)); /* 检查是否一个额外的数据包被需要给当前的数据,因为它不够大来填充整个邮箱空间Bug fix from ETG Forum / Lam Research Corporation */ /* 检查是否一个额外的字节遗漏:整个字节的数目 - 字节,它将会被传输用当前的响应 - 字节,它将会被传输在接下来整个分包 */ if ( (((n<<1)-size) - (nSdoInfoFragmentsLeft * (size + 2))) > 0) { nSdoInfoFragmentsLeft = nSdoInfoFragmentsLeft + 1; } } /*ECATCHANGE_END(V5.01) SDO4*/ else { nSdoInfoFragmentsLeft = 0; } } /* 获得需要的对象字典列表的下一部分 */ size = OBJ_GetObjectList(listType, &index, size, pData,&abort); /* 存储下一个分包的对象字典 */ nSdoInfoIndex = index; /* size邮箱缓存的字节大小在指令之前和包含邮箱响应数据的大小在下一个指令之后 */ { size = u16SendMbxSize - size - MBX_HEADER_SIZE; } } /* 邮箱响应数据的大小 */ pSdoInfoInd->MbxHeader.Length = size; if(abort == 0) { pSdoInfoInd->SdoHeader.InfoHead &= ~INFOHEAD_OPCODE_MASK; pSdoInfoInd->SdoHeader.InfoHead |= (UINT16) (SDOINFOSERVICE_OBJDICTIONARYLIST_S << INFOHEAD_OPCODE_SHIFT); /* 分段数据包的大小仍然应该被发送 */ pSdoInfoInd->SdoHeader.FragmentsLeft = SWAPWORD(nSdoInfoFragmentsLeft); if (nSdoInfoFragmentsLeft) { /* 这里仍然有分段数据被发送,InComplete符号在SDO Information respose应该被发送 */ pSdoInfoInd->SdoHeader.InfoHead &= ~ INFOHEADER_INCOMPLETE_MASK; pSdoInfoInd->SdoHeader.InfoHead |= (UINT16) (SDOINFOSERVICE_INCOMPLETE << INFOHEAD_OPCODE_SHIFT); /* 符号FRAGMENTS_FOLLOW符号需要被设置,在函数MBX_MailboxSendReq里,指示邮箱操作,仍需要符号位被发送,因此,这个函数应该被再次调用在COE_ContinueInd函数里,当真正得邮箱缓存被发送 */ flags = FRAGMENTS_FOLLOW | COE_SERVICE; } } } break; case SDOINFOSERVICE_OBJDESCRIPTION_Q://获得对象描述请求 case SDOINFOSERVICE_ENTRYDESCRIPTION_Q://获得入口描述请求 /* 获得请求的索引 */ index = SWAPWORD(pSdoInfoInd->SdoHeader.Data.Obj.Index); /* 获得所求索引的对象字典的句柄 */ pObjEntry = OBJ_GetObjectHandle( index ); if ( pObjEntry ) { /* 对象存在 */ UINT16 size = 0; if ( opCode == SDOINFOSERVICE_OBJDESCRIPTION_Q )//如果opCode是获得Object的描述请求 { /* 对象描述是被请求 */ OBJTOMBXMEMCPY(&pSdoInfoInd->SdoHeader.Data.Obj.Res, OBJ_GetObjDesc(pObjEntry), SDO_INFO_OBJ_DESC_SIZE); /* 邮箱应该足够大,以至于最大的对象字典描述,邮箱都能满足(在从站代码里面,分段是不被执行),因此它将被检查仅仅在对象字典信息满足的情况下 */ size = OBJ_GetDesc(index, 0, pObjEntry, NULL) + SIZEOF_SDOINFOOBJSTRUCT; if ( size > (u16SendMbxSize - MBX_HEADER_SIZE) ) { /* 对象字典信息的长度不适合邮箱的大小,这个对象字典信息将被发送没有名字 */ size = SIZEOF_SDOINFOOBJSTRUCT; } else { /* 对象字典的描述适合邮箱的大小,获得对象字典的名字 */ size = OBJ_GetDesc(index, 0, pObjEntry, ((UINT16 MBXMEM *) &(&pSdoInfoInd->SdoHeader.Data.Obj.Res)[1])) + SIZEOF_SDOINFOOBJSTRUCT; } } else//如果请求是获得入口描述请求 { /* 入口的描述被需要, 获得需要的子索引 */ UINT8 subindex = (UINT8) ((pSdoInfoInd->SdoHeader.Data.Entry.Info & ENTRY_MASK_SUBINDEX) >> ENTRY_SUBINDEX_SHIFT); /* 获得最大的子索引 */ UINT8 maxSubindex = (OBJ_GetObjDesc(pObjEntry)->ObjFlags & OBJFLAGS_MAXSUBINDEXMASK) >> OBJFLAGS_MAXSUBINDEXSHIFT; if ( subindex <= maxSubindex )//如果子索引,小于等于最大的子索引 { UINT16 ObjectFlags; /* 需要的子索引不是太大 */ /* 获得需要的入口的入口索引描述 */ OBJTOMBXMEMCPY(&pSdoInfoInd->SdoHeader.Data.Entry.Res, OBJ_GetEntryDesc(pObjEntry, subindex), SIZEOF(TSDOINFOENTRYDESC)); /* 传输的值的信息在例子代码里面没有支持 */ pSdoInfoInd->SdoHeader.Data.Entry.Info &= ~ENTRY_MASK_VALUEINFO; ObjectFlags = OBJ_GetObjDesc(pObjEntry)->ObjFlags; ObjectFlags = (ObjectFlags & OBJFLAGS_OBJCODEMASK) >> OBJFLAGS_OBJCODESHIFT; if ( (ObjectFlags & (OBJCODE_ARR | OBJCODE_REC)) && (subindex == 0) )//邮箱的长度不能够容纳最长的入口描述 { OBJTOMBXSTRCPY( ((UINT16 MBXMEM *) &(&pSdoInfoInd->SdoHeader.Data.Entry.Res)[1]), aSubindexDesc, SIZEOF(aSubindexDesc) ); size = 12 + SIZEOF_SDOINFO + SIZEOF(TSDOINFOENTRY); // 12: "子索引 000"的长度 } else { /* 邮箱应该足够大以至于容纳最长的入口描述 (the fragmentation is not done in the sample code), 以至于它可以被检查是否入口描述合适? */ size = OBJ_GetDesc(index, subindex, pObjEntry, NULL) + SIZEOF_SDOINFO + SIZEOF(TSDOINFOENTRY); if ( size > (u16SendMbxSize - MBX_HEADER_SIZE) ) { /* 说明对象信息是不适合邮箱的长度,这个对象描述将被发送没有名字 */ size = SIZEOF_SDOINFO + SIZEOF(TSDOINFOENTRY); } else { /* 对象描述是满足邮箱的长度,获得入口地址的名字 */ size = OBJ_GetDesc(index, subindex, pObjEntry, ((UINT16 MBXMEM *) &(&pSdoInfoInd->SdoHeader.Data.Entry.Res)[1])) + SIZEOF_SDOINFO + SIZEOF(TSDOINFOENTRY); } } } else abort = ABORTIDX_SUBINDEX_NOT_EXISTING; } if ( abort == 0 ) { {
/* 对于对象和入口描述,这个例子代码没有支持分段,这个邮箱需要足够大 */ pSdoInfoInd->SdoHeader.FragmentsLeft = 0; /* 存储邮箱数据的大小在邮箱的缓冲区 */ pSdoInfoInd->MbxHeader.Length = size; /* 设置SDO 信息响应的opCode */ pSdoInfoInd->SdoHeader.InfoHead &= ~INFOHEAD_OPCODE_MASK; pSdoInfoInd->SdoHeader.InfoHead |= (UINT16) ((opCode+1) << INFOHEAD_OPCODE_SHIFT); } } } else abort = ABORTIDX_OBJECT_NOT_EXISTING;//对象字典不存在 break; default: abort = ABORTIDX_COMMAND_SPECIFIER_UNKNOWN;//命令是不存在的 } if ( abort ) { /* 发送一个SDO信息作为错误响应 */ pSdoInfoInd->MbxHeader.Length = SIZEOF_SDOINFOERRORSTRUCT; pSdoInfoInd->SdoHeader.InfoHead &= ~INFOHEAD_OPCODE_MASK; pSdoInfoInd->SdoHeader.InfoHead |= (UINT16) ((SDOINFOSERVICE_ERROR_Q) << INFOHEAD_OPCODE_SHIFT); pSdoInfoInd->SdoHeader.FragmentsLeft = 0; pSdoInfoInd->SdoHeader.Data.Error.ErrorCode = SWAPDWORD(cAbortCode[abort]);#if SEGMENTED_SDO_SUPPORTED nSdoInfoFragmentsLeft = 0;#endif } if (MBX_MailboxSendReq((TMBX MBXMEM *) pSdoInfoInd, flags) != 0)//这是最终通过邮箱配置的SDO { /* 如果邮箱响应不能被发送(或者存储),这个响应将会存储在variable pCoeSendStored和将会自动被发送在邮箱处理函数(COE_ContinueInd)当发送邮箱将会被读在下一个周期从主站 */ pCoeSendStored = (TMBX MBXMEM *) pSdoInfoInd; } return 0;}/** @} */#endif /* COE_SUPPORTED *///一下是这个子函数的最终目的,就是把相关的信息填进去pSdoInfoInd这个结构体里面
/ /** \param listType 需要被请求的listType (0=所有的对象字典, 1=接收映射的对象字典, 2=发送映射的对象字典, 3=备份的对象字典, 4=设置的对象字典 \return 需要被请求的listtype,里面包含的对象字典数 \brief 这个函数计算对象字典数从需要的listtype * UINT16 OBJ_GetNoOfObjects(UINT8 listType) { /* 这个变量listFlags包含屏蔽码用来在Entry-Desc的,具体可以参考数据结构TSDOINFOENTRYDESC在sdoserv.h里面的,listType = 0,指示所有的对象字典都应该被计数,通过listFlags包含的屏蔽码可以得到各个对象字典的操作情况 */ UINT16 listFlags = 0x0020 << listType; /* 设置pObjEntry给object对象字典的入口地址 */ OBJCONST TOBJECT OBJMEM * pObjEntry = (OBJCONST TOBJECT OBJMEM *) COE_GetObjectDictionary(); UINT16 n = 0; while (pObjEntry != NULL) { /* 计数所需要的listtype里面对象字典的数目 */ if ( pObjEntry->Index >= 0x1000 ) { UINT8 t = listType; if ( t ) { UINT8 maxSubindex = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_MAXSUBINDEXMASK) >> OBJFLAGS_MAXSUBINDEXSHIFT; UINT16 i = 0; while ( t && i <= maxSubindex ) { if ( OBJ_GetEntryDesc(pObjEntry,(UINT8) i)->ObjAccess & listFlags ) t = 0; i++; } } if ( !t ) { /* 从listType里面的对象字典被发现 */ n++; } } /* 在object对象字典里面的下一个对象被指示 */ pObjEntry = (TOBJECT OBJMEM *) pObjEntry->pNext; }//指示下一个对象字典的入口,并进入循环对对象字典进行判断 return n;//返回的是对象字典里面对象的数目, }
/ /** \param index 指明对象字典的索引. \param subindex 指明对象字典的子索引. Subindex 0xff returns the description of the whole object ( the name of the object ). Subindex 0x00 returns the description of the subindex 0 and so on. \param pObjEntry 是一个对象字典的句柄 ( for faster access ) or 无. \param pData 是一个描述字符串的内存区间或者无( if the size of string is unknown ): \return 描述串的字节大小 (without null termination byte ). 0 将会被返回,如果一个指明的入口没有被发现. \brief 这个函数返回大小和描述串给需求的入口地址. 它可能定义一个入口地址的所有的描述串(包括一个对象里的名字)在一个结构里: <br> { <br> 带索引的对象名字, <br> 子索引的描述, <br> . <br> 子索引N的描述, <br> 0xFF <br> } * UINT16 OBJ_GetDesc( UINT16 index, UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData ) { UINT16 strSize = 0; OBJCONST UCHAR OBJMEM * pDesc = (OBJCONST UCHAR OBJMEM *) pObjEntry->pName; /* 获得ObjCode的信息和最大子索引在本地变量来支持不同的处理器 */ UINT8 objCode = (pObjEntry->ObjDesc.ObjFlags & OBJFLAGS_OBJCODEMASK) >> OBJFLAGS_OBJCODESHIFT; if ( (subindex == 0) || (objCode == OBJCODE_VAR) ) { // 获得对象字典的描述长度 strSize = OBJSTRLEN( (OBJCONST CHAR OBJMEM *) pDesc ); // 如果有指针,拷贝数据: if ( pData ) { OBJTOMBXSTRCPY( pData, pDesc, strSize ); } } else { if ( objCode == OBJCODE_REC ) { // 获得指针给subindex1的描述 : // 16位的变量来防止溢出,如果子索引是0xFF被读到 UINT16 i = 1; UINT16 tmpSubindex = subindex; #if DIAGNOSIS_SUPPORTED if(index == 0x10F3 && subindex > 6) { /*所有的diagnosis 信息都有同一个入口描述名字*/ tmpSubindex = 6; } #endif { //获得pDesc的子索引 OBJCONST UCHAR OBJMEM * pSubDesc = (OBJCONST UCHAR OBJMEM *) OBJGETNEXTSTR( pDesc ); if ( pSubDesc[0] != 0xFF && pSubDesc[0] != 0xFE )//如果pSubDesc不是最后一个子索引 { while ( i <= tmpSubindex )//如果i是小于等于subindex的值的话 { if ( i == tmpSubindex ) { strSize = OBJSTRLEN( (OBJCONST CHAR OBJMEM *) pSubDesc ); if ( pData && strSize ) OBJTOMBXSTRCPY( pData, pSubDesc, strSize );//将子索引的值拷贝到数据区 break; } else { i++; pSubDesc = (OBJCONST UCHAR OBJMEM *) OBJGETNEXTSTR( pSubDesc ); }//i自加1,pSubDesc指向的子索引,指向下一个 } } } } } if ( strSize == 0 ) { // 没有字符串发现给子索引x strSize = 12; if ( pData ) { UCHAR OBJMEM TmpDescr[13]; OBJSTRCPY(TmpDescr,aSubindexDesc,SIZEOF(TmpDescr)); OBJ_CopyNumberToString( &TmpDescr[9], subindex ); MBXSTRCPY( pData, TmpDescr, SIZEOF(TmpDescr) ); } } } return strSize; }
往死里写——SDOS_SdoInfoInd
最新推荐文章于 2023-11-30 11:04:59 发布