声明:本文涉及的开源程序代码学习和研究,严禁用于商业目的。 如有任何问题,欢迎和我交流。(企鹅号: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. Net中的常见的关键字

    Net中的关键字有很多,我们最常见的就有new.base.this.using.class.struct.abstract.interface.is.as等等.有很多的,在这里就介绍大家常见的,并且有 ...

  2. 全自动迁移数据库的实现 (Fluent NHibernate, Entity Framework Core)

    在开发涉及到数据库的程序时,常会遇到一开始设计的结构不能满足需求需要再添加新字段或新表的情况,这时就需要进行数据库迁移. 实现数据库迁移有很多种办法,从手动管理各个版本的ddl脚本,到实现自己的mig ...

  3. Windows 10 安装 Sql Server 2014 反复提示需要安装 .NET Framework 3.5 SP1 的解决方案

    一.首先安装.NET Framework 3.5: 离线安装方式: 1.装载相对应的系统安装盘,我是Windows 10 x64 企业版,所以装载Windows 10 x64 企业版安装镜像ISO,盘 ...

  4. 使用page object模式抓取几个主要城市的pm2.5并从小到大排序后写入txt文档

    #coding=utf-8from time import sleepimport unittestfrom selenium import webdriverfrom selenium.webdri ...

  5. [连载]《C#通讯(串口和网络)框架的设计与实现》- 8.总体控制器的设计

    目       录 第八章           总体控制器的设计... 2 8.1           总控制器的职能... 2 8.2           组装和释放部件... 3 8.3      ...

  6. Linux(十)___iptables防火墙

    一.防火墙的作用 三.防火墙的分类 三.iptables基本语法: 表: 常用filter,nat用于地址映射转换. 配置文件: /etc/sysconfig/iptables 过滤表信息 . 查看i ...

  7. FreeMarker的基础语法

    FreeMarker语言 FreeMarker语言概述 FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写. FreeMarker被设计用来生成HTML Web ...

  8. 《JS实现复制内容到剪贴板功能,可兼容所有PC浏览器,不兼容手机端》

    前记:本来原生的JS是有提供一个函数来实现这个功能(window.clipboardData),但是很遗憾,这个函数仅仅支持IE和FF浏览器,所以基本用处不大.下边介绍的是一个第三方插件库(ZeroC ...

  9. js url.slice(star,end) url.lastIndexOf('/') + 1, -4

    var url = '"http://60.195.252.25:15518/20151228/XXSX/作三角形的高.mp4")' document.title = url.sl ...

  10. Oracle Sales Cloud:管理沙盒(定制化)小细节1——利用公式创建字段并显示在前端页面

    Oracle Sales Cloud(Oracle 销售云)是一套基于Oracle云端的CRM管理系统.由于 Oracle 销售云是基于 Oracle 云环境的,它与传统的管理系统相比,显著特点之一便 ...