声明:本文涉及的开源程序代码学习和研究,严禁用于商业目的。 如有任何问题,欢迎和我交流。(企鹅号:408797506)

本文介绍自己用过的ACS,其中包括开源版(提供下载包)和商业版(仅提供安装包下载,没有源码)

参考:

1) http://www.docin.com/p-1306443672.html

2) http://www.easycwmp.org/

一. 背景

  程序设计的思想来自于easycwmp官网,看过或者用过easycwmp的工程师应该都知道,该开源代码还有商业版,而且价格不菲(前公司曾经想要购买后放弃),easycwmp官网如是说:DataModel is developped with shell as free solution and with C as commercial solution.。开源代码用来学习还是值得的,若是用于商业产品可能就会显得"力不从心"。幸运的是,我有机会阅读了Works Systems公司和broadcom公司的tr069代码,架构设计与easycwmp的设计"如出一辙",下图是easycwmp官网的程序架构图。但是,个人对于Works Systems公司的代码"情有独钟"并且进行了重新开发和利用,使得程序更加高效易用和移植性。基于此,本文就着重介绍在商业代码中如何高效,便捷的实现DataModel 和CWMP core分离,给读者一个程序设计的思路。 后续若有机会可以介绍一下broadcom公司的程序设计仅供参考。本质上大同小异。

二. 程序设计概要

  对于单一的产品线,程序采用"单进程多线程"的思想。这样比较容易简洁,而且方便维护。若是多功能的产品,即一个设备上需要运行多个"CWMP进程", 那么我们使用了创建"多个子进程"的方法,每个子进程根据配置文件的不同从而实现设备的不同需求(该功能待完善)。比如现在家庭或者企业网关产品越来越需要"智慧""智能"的需求,如何让设备与手机互连互通,保证安全方便高效的前提下,既可以被运营商(卖方)管理,同时又可以被自己(买方)控制管理,这是一个值得思考的问题,也是工程师需要考虑的技术。

  下图是单一产品的tr069程序处理流程。大致分为:配置文件解析模块,日志模块,设备xml解析模块,任务模块以及事件处理模块(有关联),多线程模块(可插入模块)等。

原则上,CWMP core的程序代码不需要修改,主要是根据客户的需求修正或者进行"插入式的"新增事件类型和模块化处理。而设备相关的程序,我们封装成了一个动态库(libcwmp.so),便于独立编译和维护开发。

(附: 高清PDF版下载路径http://download.csdn.net/detail/eryunyong/9731487

3.1 配置文件解析(libconf)

根据配置文件的全路径和内容初始化数据结构,使用例子如下:

 char      conf_file[PATH_MAX] = {};
conf_t *tmp = NULL; tmp = conf_load(conf_file);
count = conf_get_int(tmp, "global:count", );

3.2 日志模块

  为了便于和Linux的syslog统一和管理,这里定义的日志等级与syslog一致。 

CWMP_LOG_EMERG        ---------------------->        EMERG = 0
   CWMP_LOG_ALERT        ---------------------->         ALERT = 1
   CWMP_LOG_CRIT        ---------------------->           CRIT  = 2
   CWMP_LOG_ERROR        ---------------------->        ERROR = 3
   CWMP_LOG_WARN        --------------------->          WARN  = 4
   CWMP_LOG_NOTICE        --------------------->        NOTICE= 5
   CWMP_LOG_INFO        --------------------->            INFO  = 6
   CWMP_LOG_DEBUG        --------------------->        DEBUG = 7

3.3 XML解析模块

使用libexpat库函数解析设备XML格式文件,以及CWMP和ACS之间交换的SOAP消息。device.xml文件内容如下:

Normal
0

7.8 磅
0
2

false
false
false

EN-US
ZH-CN
X-NONE

MicrosoftInternetExplorer4

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Times New Roman","serif";}

 <TR069 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<trf>
<obj name="InternetGatewayDevice">
<param name="DeviceSummary" getval_func="CpeGetDeviceSummary"></param>
<param name="LANDeviceNumberOfEntries" type="" getval_func="CpeGetLANDeviceNumberOfEntries"></param>
<param name="WANDeviceNumberOfEntries" type="" getval_func="CpeGetWANDeviceNumberOfEntries"></param>
<obj name="DeviceInfo" noti_rw="">
<param name="SpecVersion" getval_func="CpeGetDeviceInfoSpecVersion"/>
<param name="HardwareVersion" getval_func="CpeGetDeviceInfoHardwareVersion"></param>
<param name="SoftwareVersion" getval_func="CpeGetDeviceInfoSoftwareVersion"></param>
<param name="Manufacturer" getval_func="CpeGetDeviceInfoManufacturer"></param>
<param name="SerialNumber" getval_func="CpeGetDeviceInfoSerialNumber"></param>
<param name="ManufacturerOUI" getval_func="CpeGetDeviceInfoManufacturerOUI"></param>
<param name="ProvisioningCode" rw="" getval_func="CpeGetDeviceInfoProvisioningCode" setval_func="CpeSetDeviceInfoProvisioningCode"></param>
<param name="ProductClass" getval_func="CpeGetDeviceInfoProductClass"></param>
<param name="DeviceType" getval_func="CpeGetDeviceInfoDeviceType"></param>
<param name="ModelName" getval_func="CpeGetDeviceInfoModelName"></param>
<param name="CpeWANAddress" noti_rw="" rw="" getval_func="CpeGetCpeWANAddress" setval_func="CpeSetCpeWANAddress"/>
</obj>
<obj name="ManagementServer">
<param name="ConnectionRequestURL" getval_func="CpeGetManagementServerConnectionRequestURL"></param>
<param name="ConnectionRequestUsername" rw="" noti_rw="" getval_func="CpeGetManagementServerConnectionRequestUsername" setval_func="CpeSetManagementServerConnectionRequestUsername"></param>
<param name="ConnectionRequestPassword" rw="" noti_rw="" getval_func="CpeGetManagementServerConnectionRequestPassword" setval_func="CpeSetManagementServerConnectionRequestPassword"></param>
<param name="Username" rw="" noti_rw="" getval_func="CpeGetManagementServerUsername" setval_func="CpeSetManagementServerUsername"></param>
<param name="Password" rw="" noti_rw="" getval_func="CpeGetManagementServerPassword" setval_func="CpeSetManagementServerPassword"></param>
<param name="ParameterKey" getval_func="CpeGetManagementServerParameterKey" setval_func="CpeSetManagementServerParameterKey"/>
<param name="URL" rw="" noti_rw="" getval_func="CpeGetManagementServerUrl" setval_func="CpeSetManagementServerUrl"></param>
<param name="PeriodicInformEnable" rw="" noti_rw="" type="" getval_func="CpeGetManagementServerPeriodicInformEnable" setval_func="CpeSetManagementServerPeriodicInformEnable"></param>
<param name="PeriodicInformInterval" rw="" noti_rw="" type="" getval_func="CpeGetManagementServerPeriodicInformInterval" setval_func="CpeSetManagementServerPeriodicInformInterval"></param>
<param name="PeriodicInformTime" rw="" type="" getval_func="CpeGetManagementServerPeriodicInformTime" setval_func="CpeSetManagementServerPeriodicInformTime"></param>
</obj> <obj name="Time">
<param name="Enable" rw="" type="" getval_func="CpeGetTimeEnable" setval_func="CpeSetTimeEnable"></param>
<param name="NTPServer1" rw="" getval_func="CpeGetTimeNTPServer1" setval_func="CpeSetTimeNTPServer1"></param>
<param name="CurrentLocalTime" type="" getval_func="CpeGetTimeCurrentLocalTime"></param>
</obj>
<obj name="X_CT-COM_MonitorCollector">
<param name="Enable" noti_rw="" rw="" type="" getval_func="CpeGet_MonitorEnable" setval_func="CpeSet_MonitorEnable"></param>
<obj name="MonitorConfig" rw="" addobj_func="TRF_Add_MonitorConfig" delobj_func="TRF_Del_MonitorConfig" refresh_func="TRF_Refresh_MonitorConfig">
<obj name="">
<param name="ParaList" noti_rw="" rw="" getval_func="CpeGet_MonitorConfig_ParaList" setval_func="CpeSet_MonitorConfig_ParaList"></param>
<param name="TimeList" rw="" type="" getval_func="CpeGet_MonitorConfig_TimeList" setval_func="CpeSet_MonitorConfig_TimeList"></param>
</obj>
</obj>
</obj>
<obj name="LANDevice">
<obj name="">
<param name="LANEthernetInterfaceNumberOfEntries" type="" getval_func="CpeGetLANEthernetInterfaceNumberOfEntries"/>
</obj>
</obj>
<obj name="ObjTest" rw="" addobj_func="TRF_Add_ObjTest" delobj_func="TRF_Del_ObjTest" refresh_func="TRF_Refresh_ObjTest">
<obj name="">
<param name="TestEnabled" rw="" type="" getval_func="CpeGetObjTest_TestEnabled" setval_func="CpeSetObjTest_TestEnabled"/>
</obj>
</obj>
</obj>
</trf> <devlib name="/usr/lib/libcwmp.so"></devlib> <auth name="dev_get_auth"></auth> <listenport name="dev_get_listenport"></listenport>
<wanparamname name="dev_get_wanparam_name"></wanparamname> <bootstrap name="dev_bootstrap"></bootstrap>
<init name="dev_init"></init>
<reboot name="dev_reboot"></reboot> <factoryreset name="dev_factoryreset"></factoryreset>
<download name="dev_download"></download>
<acsstatus name="dev_set_acs_status"></acsstatus>
<urldnsresolve name="dev_url_dns_resolve"></urldnsresolve> <upload name="dev_upload"></upload>
<cwmpenable name="dev_cwmp_enable"/> <informlist>
<inform name="InternetGatewayDevice.DeviceInfo.ModelName"/>
<inform name="InternetGatewayDevice.DeviceInfo.DeviceType"/>
</informlist> <eventlist>
<event name="X CT-COM BIND"></event>
</eventlist> </TR069>

InternetGatewayDevice是整个参数树的根。obj表示这是一个对象,obj可以读,可以写,当obj的name为0时,表示该obj可以是个模板,为创建后面的实例提供一个模板,当ACS查询时,不会把obj name为0的Obj发送给ACS。Obj的rw=1,表示该obj可以添加子obj,通过addobj_func来添加,通过delobj_func来删除,refresh_func表示刷新该obj下的信息。 Obj的noti_rw =1认为可以设置该obj的属性,譬如notify属性,如果设置了obj的属性,则认为该obj下的所以子树都有该属性。Param表示一个参数项,参数可以读,可以写,通过getval_func来读,通过setval_func来写。noti_rw=1认为可以设置该Param的属性,譬如notify属性。Type的含义如下:

string             ------------------------->         0
int                -------------------------->         1
unsigned int       ----------------------->         2
bool               ------------------------->         3
datetime           ----------------------->         4
base64             ----------------------->         5
long               ------------------------->         6
unsigned long      ---------------------->         7
hex binary         ----------------------->         8
object             ------------------------->         9

3.4 TASK任务模块

根据任务队列中的消息类型进行处理,把需要发送给ACS的事件event消息加入事件队列。

 //诊断
#define TASK_DIAG 1
//重启
#define TASK_REBOOT 2
//恢复出厂设置
#define TASK_FACTORY 3
//download
#define TASK_DOWNLOAD 4
//upload
#define TASK_UPLOAD 5
//change ACS URL
#define TASK_CHANGE_ACS_URL 6
#define TASK_SUBDEVICE 7
#define TASK_ADD_EVENT 8
#define TASK_ADD_INFORM 9
#define TASK_CLEAR_EVENT 10 #define TASK_VPN_RESTART 20
#define TASK_SYSLOG_RESTART 21
#define TASK_FIREWARE_RESTART 22 #define TASK_OTHER 99

3.5 事件处理模块

  使用event_handle函数来处理事件,通过信号量来等待是否需要处理的事件,以及从事件队列中获取处理的事件。同理,在其他线程函数中,通过置信号量,将事件加入队列中来通知该模块处理。

STATUS_IDLE = 0,        /* 空闲状态 */
    STATUS_INIT,            /* 初始化,获取ACS的URL,以及发送Inform消息*/
    STATUS_CONN,            /* CPE和ACS处于连接状态,并处理ACS下发的任务 */
    STATUS_ERROR,         /* 发生错误 */
    STATUS_FINS,          /* 结束事件处理 */

3.6 其他多线程模块

   为了实现"低耦合高内聚"的模块化思想,程序设计采用了多线程来实现。比如:周期上报Inform事件, 根据tr069规范监测参数变化的上报事件,检测WAN口地址变化的事件,STUN线程,DHCP发现ACS地址事件。

四. 数据结构

1) cwmp_context结构体是CWMP进程处理的上下文,主要包括初始化设备参数树,Value change,监视参数变化,记录事件等。

 struct cwmp_context{
file_context_t file_ctx; //配置文件
trf_param_t param_root; //参数树根节点
dev_info_t dev_info; //设备信息 void *handle_lib; //设备library的handle int acs_port; //监听ACS的端口
int acs_retrycount; //连接ACS重试次数
int notify_interval; //监视参数变化的间隔时间 pthread_mutex_t mutex_attr;
hash_t *ht_attr; //记录参数属性,需要上报的。 pthread_mutex_t mutex_val_change;
hash_t *ht_val_change; //记录Value Change char **inform_array; //需要上报的参数项数组
int inform_count; //需要上报的参数项总数 pthread_mutex_t mutex_inform_tmp;
char **inform_array_tmp; //临时需要上报的参数项数组
int inform_count_tmp; //临时需要上报的参数项总数 pthread_mutex_t mutex_evt;
int evt_count;
event_info_t *evt_array; //记录事件
event_global_t evt_global_info; //记录由于重启需要保存的信息
sem_t sem_send_acs; //发送给acs信息的信号量 pthread_mutex_t mutex_task; //for task_list
list_t *task_list; //task list
sem_t sem_task; pthread_mutex_t mutex_param; //对参数进行加锁,防止多线程操作时导致程序不稳定
//仅在对参数有操作的线程中加锁
trans_t transfer_info; //用于Download和Upload
};

2)EventType主要定义了规范中的事件类型

 typedef enum
{
EVENT_BOOTSTRAP = ,
EVENT_BOOT,
EVENT_PERIODIC,
EVENT_SCHEDULED,
EVENT_VALUECHANGE,
EVENT_KICKED,
EVENT_CONNECTIONREQUEST,
EVENT_TRANSFERCOMPLETE,
EVENT_DIAGNOSTICSCOMPLETE,
EVENT_REQUESTDOWNLOAD,
EVENT_AUTONOMOUSTRANSFERCOMPLETE,
EVENT_MREBOOT,
EVENT_MSCHEDULEINFORM,
EVENT_MDOWNLOAD,
EVENT_MUPLOAD,
EVENT_MAXCOUNT
}EventType;

对于自定义的事件类型可以通过xml中如下定义:

<eventlist>
      <event name="X CT-COM BIND"></event>
   </eventlist>

3)

 struct trf_param
{
char name[PARAM_NAME_LEN+]; //参数名
int type; //参数类型 trf_datatype_e
int writable; //是否可写。0:不可写,1:可写,如果object
//可以Add,则可写
int max_instance; //属于Object, 最大instance值,-1表示无限制
int notification; //属于Parameter, 0:off,1:passive,2:active
unsigned char noti_rw; //属于Parameter, 是否可以设置上报属性,0 不可以 1 可以
unsigned long acl; /*属于Parameter, access list */
TRFGetParamValueFunc getparamval_func; //属于Parameter, 取得参数值函数
TRFSetParamValueFunc setparamval_func; //属于Parameter, 设置参数值函数
TRFAddObjectFunc addobject_func; //属于Object, AddObject
TRFDelObjectFunc delobject_func; //属于Object, DeleteObject
TRFRefreshFunc refresh_func; //属于Object, 刷新
struct trf_param *parent; //父节点
struct trf_param *child; //子节点
struct trf_param *nextSibling; //兄弟节点
};

定义树形结构的节点,每个节点拥有自己的属性和方法。

4) 设备相关函数

 <devlib name="/usr/lib/libcwmp.so"/>                          Libary的位置
<auth name="dev_get_auth"/> 是否需要开启ACS的认证
<listenport name="dev_get_listenport"/> CWMP的监听端口
<wanparamname name="dev_get_wanparam_name"/> 取得WAN口的参数项名称的全路径
<bootstrap name="dev_bootstrap"/> 判断是否取得首次连接到ACS的标志
<init name="dev_init"/> 初始化设备操作
<reboot name="dev_reboot"/> 设备的reboot方法
<download name="dev_download"/> 设备的download的方法,包括下载,升级之类的方法
<upload name="dev_upload"/> 设备upload的方法,包括生成配置文件,上传日志等方法
<cwmpenable name="dev_cwmp_enable"/> 判断是否启动CWMP进程
<urldnsresolve name="dev_url_dns_resolve"></urldnsresolve> ACS的URL解析
...
其他参数项相关的操作函数

五. 总结

CWMP core与Datamodel分离,通过不断的调试和实践,并应用于不同的运营商(电信,联通,移动),使得CWMP core的程序不断成熟和稳定。

对于相同的功能,我们仅仅需要修改device.xm就可以实现需求,而不用去修改代码;

对于新增的参数项或者节点开发,我们仅需要开发设备相关的库;

对于新增的事件或者ACS下发的任务,修改library的同时我们只需要稍微修改CWMP core的程序就可以达到目的;

对于新增模块,我们采用线程"插入"的思想来实现,而不用修改程序的主体架构。

六. 参考

1.http://www.docin.com/p-1306443672.html

2. http://www.easycwmp.org/

800x600

Normal
0

7.8 磅
0
2

false
false
false

EN-US
ZH-CN
X-NONE

MicrosoftInternetExplorer4

/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Times New Roman","serif";}

CWMP开源代码研究5——CWMP程序设计思想的更多相关文章

  1. CWMP开源代码研究7——cwmp移植

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...

  2. CWMP开源代码研究——git代码工程

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...

  3. CWMP开源代码研究1——开篇之作

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...

  4. CWMP开源代码研究6——libcwmp动态库开发

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 为了使程序具有通用性,便于扩展和维护.采用了"模块"插入的思想.将设备业务相 ...

  5. CWMP开源代码研究2——easycwmp安装和学习

    声明:本文是对开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅号:408797506) 本文所有笔记和代码可以到csdn下载:http://download.csdn.n ...

  6. CWMP开源代码研究4——认证流程

    TR069 Http Digest 认证流程   一 流程及流程图 1.1盒端主动发起Http Digest认证流程  盒端CPE                                    ...

  7. CWMP开源代码研究3——ACS介绍

    声明:本文涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅号:408797506) 本文介绍自己用过的ACS,其中包括开源版(提供下载包)和商业版(仅提供安装包下载 ...

  8. CWMP开源代码研究——cwmp移植

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...

  9. CWMP开源代码研究——stun的NAT穿透

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 参考: http://www.cnblogs.com/myblesh/p/6259765.htm ...

随机推荐

  1. Oracle逻辑迁移某业务用户及数据

    1.确定基本信息 2.源数据库导出 3.目的数据库导入 4.逻辑迁移注意事项 1.确定基本信息 确定基本信息: 源数据库所在系统类型:________ 源数据库地址:__.__.__.__ 源数据库版 ...

  2. markdown常用语法总结

    转自markdown示例[模板] 1.1.段落标题 根据原文中的文档标题可以对应设置标题. # 一级标题## 二级标题### 三级标题 效果 => 一级标题 二级标题 三级标题 1.2.斜体.加 ...

  3. Visual studio 通用开发环境配置:SDL,FFMPEG为例

    引言 每一个C++库的使用都是从开发环境的配置开始的,其实每个库的配置过程都是大同小异,总结下来有下面几个步骤: 下载库文件,这里假定是已经预先编译完成的. 配置库文件的包含目录(include)和库 ...

  4. JavaScript: 零基础轻松学闭包

    本文面向初学者,大神轻喷. 闭包是什么? 初学javascript的人,都会接触到一个东西叫做闭包,听起来感觉很高大上的.网上也有各种五花八门的解释,其实我个人感觉,没必要用太理论化的观念来看待闭包. ...

  5. CloudNotes之领域建模篇:领域模型简介

    CloudNotes领域模型还是相对简单的,并不一定需要采用面向领域驱动的设计方法来解决CloudNotes的领域问题.但出于以下几个方面的原因,我还是采用了面向领域驱动的方式来开发CloudNote ...

  6. php内核分析(一)-sapi_module_struct

    这里阅读的php版本为PHP-7.1.0 RC3,阅读代码的平台为linux 首先是寻找php的入口,php有很多种模式,apache,php-fpm, cli模式,我要入手的话,只能先从最简单的cl ...

  7. [占位-未完成]scikit-learn一般实例之十二:用于RBF核的显式特征映射逼近

    It shows how to use RBFSampler and Nystroem to approximate the feature map of an RBF kernel for clas ...

  8. EF里单个实体的增查改删以及主从表关联数据的各种增删 改查

    本文目录 EF对单个实体的增查改删 增加单个实体 查询单个实体 修改单个实体 删除单个实体 EF里主从表关联数据的各种增删改查 增加(增加从表数据.增加主从表数据) 查询(根据主表找从表数据.根据从表 ...

  9. 如何在Spring MVC Test中避免”Circular view path” 异常

    1. 问题的现象 比如在webConfig中定义了一个viewResolver public class WebConfig extends WebMvcConfigurerAdapter { //配 ...

  10. python序列,字典备忘

    初识python备忘: 序列:列表,字符串,元组len(d),d[id],del d[id],data in d函数:cmp(x,y),len(seq),list(seq)根据字符串创建列表,max( ...