采用掩码方式简化产品国家地区支持能力的表示
一、背景描述
某系列产品中,不同产品、国家和地区支持不同的配置项(但差异不大)。各配置项均由其BranchLeaf结点值(BLV)唯一标识。
作为ONU通用配置媒介之一,某模块对各配置项创建合法性校验函数IsBranchLeafValid(…),其中包含的结点列表表示产品缺省支持的所有配置项,类似“白名单”;此外根据各国家地区的要求创建屏蔽函数IsBranchLeafScreened (…),其中包含的结点列表表示该国家/地区不予支持的配置项,类似“黑名单”。其中,“黑名单”列表为“白名单”列表的子集。两个名单结合起来对接收到的OLT配置帧进行校验,从而表现出不同的支持能力。
因此,对于M个产品,N个国家地区,在物理上将需要M+N个黑白名单。具体实现上,黑白名单函数内充斥着大量if...else与switch...case结构:
1 /* 每个产品对应一个Product_Adapter.c文件,内含IsBranchLeafValid实现 */ 2 BOOL IsBranchLeafValid(OAM_BRANCH_LEAF eBranchLeaf) 3 { 4 BOOLEAN retcode = 1; 5 switch(eBranchLeaf) 6 { 7 case OnuSn: 8 case FirmwareVer: 9 case ChipsetID: 10 case EthDSRateLimit: 11 case QosConfig: 12 case FastLeaveState: 13 case FastLeaveCtrl: 14 case FaxModemConf: 15 case SIPDigitMap: 16 //Dozens of eBranchLeaf... ... 17 { 18 retcode = 1; 19 } 20 break; 21 22 default: 23 { 24 retcode = 0; 25 } 26 break; 27 } 28 29 return retcode; 30 } 31 32 33 BOOL IsBranchLeafScreened(INT32U dwdwdwRegionVer, OAM_BRANCH_LEAF eBranchLeaf) 34 { 35 if(dwdwRegionVer == DEFAULT_XINJIANG_CFG) 36 { 37 switch(eBranchLeaf) 38 { 39 case VlanConfig: 40 41 case FaxModemConf: 42 case SIPDigitMap: 43 //Dozens of eBranchLeaf... ... 44 return 1; 45 default: 46 return 0; 47 } 48 } 49 else if(dwdwRegionVer == DEFAULT_JIANGSU_CFG) 50 { 51 switch(eBranchLeaf) 52 { 53 case VlanConfig: 54 return 1; 55 default: 56 return 0; 57 } 58 } 59 else 60 { 61 return 0; 62 } 63 }
后续若增加其他产品、国家和地区,需要不断增加文件或扩展函数。此外,各名单的BLV列表内容互有重叠,代码冗余度很高。
二、改进方案
定义产品、国家、地区掩码,掩码中各比特表示某BLV的相应产品、国家和地区支持情况。这里将国家和地区分开是为了扩展掩码支持范围。
例如下面的简化格式(前两比特表示产品支持,后两比特表示地区支持):
对于某BLV,上面的0b'1001表示F420V2上海版本支持该配置项,以此类推。
这样就无需IsBranchLeafScreened和IsBranchLeafValid黑白名单函数,校验时只需根据掩码对应比特(偏移)来判断相应的BLV是否支持。
对于产品掩码,可在预编译阶段设置掩码偏移值;对于国家和地区,可在ONU启动初始化时通过查询国家地区码来设置掩码偏移值。
三、实践情况
目前已定义的EPON SFU产品有F420V2/F411V2/F420G/F400G/F1420/F460/F460V2/F460M等等,国家地区码(非指本文“掩码”)如下:
1 /* 国家码 */ 2 #define DEFAULT_CFG 0 /* 基本默认配置文件 */ 3 #define DEFAULT_RUSSIA_CFG 1 /* 俄罗斯默认配置文件 */ 4 #define DEFAULT_LITHUANIA_CFG 2 /* 立陶宛默认配置文件 */ 5 //… … 6 7 /* 地区码 */ 8 #define DEFAULT_JIANGSU_CFG 200 /* 江苏省默认配置文件 */ 9 #define DEFAULT_XINJIANG_CFG 201 /* 新疆默认配置文件 */ 10 #define DEFAULT_HAINAN_CFG 202 /* 海南默认配置文件 */ 11 #define DEFAULT_TIANJIN_CFG 203 /* 天津默认配置文件 */ 12 #define DEFAULT_ANHUI_CFG 204 /* 安徽默认配置文件 */ 13 #define DEFAULT_SHANGHAI_CFG 205 /* 上海默认配置文件 */ 14 //… …
因此,可将产品掩码定义为1字节,掩码偏移比特定义如下:
1 /*ONU产品掩码定义(根据新增产品而扩展)*/ 2 #if defined(CONFIG_CSP_PRODUCT_F411) || defined(CONFIG_CSP_PRODUCT_F420) || defined(CONFIG_CSP_PRODUCT_F420G) 3 #define PRODUCT_SHIFT 0 4 #elif defined(CONFIG_CSP_PRODUCT_F400G) 5 #define PRODUCT_SHIFT 1 6 #elif defined(CONFIG_CSP_PRODUCT_F1420) 7 #define PRODUCT_SHIFT 2 8 #else 9 #define PRODUCT_SHIFT 3 10 #endif
国家掩码定义为4字节长整型,从低位到高位依次对应国家码编号,即掩码bit-0表示缺省版本支持情况(恒为1),bit-1表示俄罗斯版本支持情况;地区掩码与国家掩码类似,应注意掩码bit-0恒为1以避免与国家掩码冲突(只能有一个缺省版本),bit-1表示江苏版本支持情况。国家、地区掩码偏移比特定义如下:
1 VOID GetRegionMask(INT32U dwRegionVer, INT8U *pucNatShift, INT8U *pucRegShift) 2 { 3 if(dwRegionVer < DEFAULT_JIANGSU_CFG) 4 *pucNatShift = dwRegionVer; 5 else 6 *pucRegShift = dwRegionVer - 199; 7 }
具体掩码结构如下OAM_CMD_MAP结构定义如下图所示:
1 typedef FUNC_STATUS(*OAM_GET_CMD_HANDLER)(OAM_HEAD_INFO*, INT8U*, INT32U*, CTC_OAM_ACK*); 2 typedef FUNC_STATUS(*OAM_SET_CMD_HANDLER)(OAM_HEAD_INFO*, INT8U*, INT32U*, CTC_OAM_ACK*); 3 typedef struct{ 4 OAM_BRANCH_LEAF eBranchLeaf; 5 INT8U ucProductMask; 6 INT32U dwNationMask; 7 INT32U dwRegionMask; 8 OAM_GET_CMD_HANDLER fnGetCmdHandler; 9 OAM_SET_CMD_HANDLER fnSetCmdHandler; 10 }OAM_CMD_MAP; 11 12 #define DEF_8U 0xFF 13 #define DEF_32U 0xFFFFFFFF 14 #define GC(NAME) OAM_##NAME##_Get_Cmd 15 #define SC(NAME) OAM_##NAME##_Set_Cmd 16 17 /*OAM命令处理函数映射表 */ 18 OAM_CMD_MAP gOamCmdFuncMap[] = { 19 {OnuSn, DEF_8U, DEF_32U, DEF_32U, GC(ONU_SN), NULL}, 20 {FirmwareVer, DEF_8U, DEF_32U, DEF_32U, GC(FirmwareVer), NULL}, 21 {ChipsetID, DEF_8U, DEF_32U, DEF_32U, GC(ChipsetID), NULL}, 22 {EthDSRateLimit, DEF_8U, DEF_32U, DEF_32U, GC(EthDSRateLimit), SC(EthDSRateLimit)}, 23 {QosConfig, DEF_8U, DEF_32U, DEF_32U, GC(QosConfig), SC(QosConfig)}, 24 {FastLeaveState, DEF_8U, DEF_32U, 0xFFFFFFBF, GC(FastLeaveState), NULL}, 25 {FastLeaveCtrl, DEF_8U, DEF_32U, 0xFFFFFFBF, NULL, SC(FastLeaveCtrl)}, 26 {FaxModemConf, 0xFD, DEF_32U, 0xFFFFFFBD, GC(FaxModemConf), SC(FaxModemConf)}, 27 {SIPDigitMap, 0xFD, DEF_32U, 0xFFFFFFBB, NULL, SC(SIPDigitMap)}, 28 //... ... 29 }; 30 const INT32U gOamCmdMapNum = sizeof(gOamCmdFuncMap) / sizeof(OAM_CMD_MAP);
其中,第一列为BLV结点值,第2~4列分别为产品、国家、地区掩码,后2列为处理函数指针。
例如。BranchLeaf名FaxModemConf对应的地区掩码为0xFFFFFFBD(1...10111101)表示江苏省和上海地区不支持该配置项。产品掩码含义与之类似。校验时黑白名单由下面的函数代替:
1 BOOL IsBranchLeafSupported(OAM_BRANCH_LEAF eBranchLeaf) 2 { 3 INT32U dwMapIdx = 0; 4 for(dwMapIdx = 0; dwMapIdx < gOamCmdMapNum; dwMapIdx++) 5 { 6 if(eBranchLeaf == gOamCmdFuncMap[dwMapIdx].eBranchLeaf) 7 break; 8 9 } 10 if(dwMapIdx == gOamCmdMapNum) 11 { 12 CtcOamLog(FUNCTION_Oam,"Unknown BranchLeaf(0x%08X)!\n", eBranchLeaf); 13 return FALSE; 14 } 15 16 if(GET_BIT(gOamCmdFuncMap[dwMapIdx].ucProductMask, PRODUCT_SHIFT) //也可先&&后GET_BIT 17 && GET_BIT(gOamCmdFuncMap[dwMapIdx].dwNationMask, gdwRegionVer.ucNatShift) 18 && GET_BIT(gOamCmdFuncMap[dwMapIdx].dwRegionMask, gdwRegionVer.ucRegShift)) 19 return TRUE; 20 21 return FALSE; 22 }
四、总结说明
1. 采用掩码的表示方法,可极大地消除冗余度和增强扩展性。
可支持10个产品、32个国家及31个地区的不同配置能力,已支持5个产品、1个国家(缺省)和两个地区,后续新增产品、国家、地区只需要修改掩码值即可,无需增加代码。
2. 相比原有做法,掩码方式在可读性和易用性方面稍嫌不足,可编写掩码的生成和解析函数或工具予以弥补。