声明:原创作品,严禁用于商业目的。 如有任何技术问题,欢迎和我交流:19826269205(微信同号)。
腾讯课堂链接
B站免费视频部分
- 1. TR069(CWMP)程序设计思想
- 2. 架构分析
- 3. TR069程序设计
- 3.1 配置文件解析(libconf)
- 3.2 日志模块
- 3.3 XML解析模块
- 3.4 TASK任务模块
- 3.5 事件处理模块
- 3.6 其他线程
- 4. 数据结构设计
- 4.1 cwmp_context结构体
- 4.2 EventType
- 4.3 设备业务相关
- 5. 总结
程序架构设计的思想来自于easycwmp官网,看过或者用过easycwmp的工程师应该都知道,该开源代码还有商业版,而且价格不菲。easycwmp官网如是说:DataModel is developped with shell as free solution and with C as commercial solution.。开源代码用来学习还是值得的,若是用于商业产品可能就会显得"力不从心"。具体原因在前面的easycwmp商业源码和免费源码对比中可见,那么如何开发可以商业使用的TR069模块,并且实现DataModel 和CWMP core分离,以便于快速开发业务需求。本篇介绍如何实现这样的系统模块,并且这样的系统模块在主流OEM厂商的研发中非常实用。
网管系统目前在一些主流的家用路由、企业网关、语音电话等网络产品中应用广泛。我们可以把TR069部分做一个参考系统名为“TR069平台”,属于远程网管系统的一个子系统。主要功能是通过TR069(CWMP)协议基于可靠链接的TCP协议来远程管理CPE设备。本系统在系统中的位置如图4.1所示。
TR069主体架构协议与设备数据模型层进行分离,达到各产品使用相同的框架,设备相关的编译成一个动态Library或者静态库形式。通过不断的实践,可以使TR069主体框架不断成熟。在国内,可以实现对于同款产品供应给不同的通讯商(电信,移动,联通等),相同的功能,通过可能仅需要改变节点文件XML,基本上不需要改变协议代码。这样,可以快速实现产品的移植开发,更好的维护产品升级。
(1) 对于协议的扩展的分析
对于TR069协议来说,有些功能是可以扩展的。比如事件,可以定义协议规范的上报事件,也可以根据运营商实现私有化或者自定义的事件。比如数据模型的方法,也可以扩展自定义的节点和相应的RPC操作方法等。这些都需要在技术开发实现过程中需要分析清楚。
(2) 对于不同设备,不同产品线差异的分析
需要在相应的XML配置文件中体现出来。
(3) 对于不同TR069客户端之间冲突的分析
在通过TR069改变系统配置的时候,考虑使用进程间互斥来避免发生冲突。
(4) 对于协议开发过程的依赖库分析
对于TR069协议层,主要依赖于网络通信库libcurl和XML文件解析库libexpat,帮助我们简化实现协议层的源码编写工作。,而DataModel数据层,根据自身产品的业务特点,可以使用uci存储、sqlite数据库等实现产品的业务功能开发。
(5) Stun协议支持分析
STUN是RFC3489规定的一种NAT穿透方式,对应于TR111协议,本系统平台采用独立线程运行,支持公网上的网管平台连接,后面会有专门介绍如何实现STUN的CPE侧开发。
tr069程序的单进程处理流程。大致分为:配置文件解析模块,日志模块,设备xml解析模块,任务模块以及事件处理模块(有关联),多线程模块(可插入模块)等。
原则上,CWMP core的程序代码不需要修改,主要是根据客户的需求修正或者进行"插入式的"新增事件类型和模块化处理。而设备相关的程序,我们封装成了一个动态库(libcwmp.so),便于产品的独立编译和维护开发。
根据配置文件的全路径和内容初始化数据结构,使用例子如下:
char conf_file[PATH_MAX] = {0};
conf_t *tmp = NULL;
tmp = conf_load(conf_file);
count = conf_get_int(tmp, "global:count", 0);
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
使用libexpat库函数解析设备XML格式文件,以及CWMP和ACS之间交换的SOAP消息。device.xml文件内容如下:
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 -------------------------> 93.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 993.5 事件处理模块
使用event_handle函数来处理事件,通过信号量来等待是否需要处理的事件,以及从事件队列中获取处理的事件。同理,在其他线程函数中,通过置信号量,将事件加入队列中来通知该模块处理。
STATUS_IDLE = 0,
STATUS_INIT,
STATUS_CONN,
STATUS_ERROR,
STATUS_FINS,
3.6 其他线程
为了实现"低耦合高内聚"的模块化思想,程序设计采用了多线程来实现。比如:周期上报Inform事件, 根据tr069规范监测参数变化的上报事件,检测WAN口地址变化的事件,STUN线程,DHCP发现ACS地址事件。
4. 数据结构设计 4.1 cwmp_context结构体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
};
4.2 EventType
EventType主要定义了规范中的事件类型:
typedef enum
{
EVENT_BOOTSTRAP = 0,
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中如下定义:
定义树形结构的节点,每个节点拥有自己的属性和方法。
4.3 设备业务相关5. 总结Libary的位置 是否需要开启ACS的认证 CWMP的监听端口 取得WAN口的参数项名称的全路径 判断是否取得首次连接到ACS的标志 初始化设备操作 设备的reboot方法 设备的download的方法,包括下载,升级之类的方法 设备upload的方法,包括生成配置文件,上传日志等方法 判断是否启动CWMP进程 ACS的URL解析 ...
CWMP core与Datamodel分离,通过不断的调试和实践,并应用于不同的运营商(电信,联通,移动),使得CWMP core的程序不断成熟稳定。
对于相同的功能,我们仅仅需要修改device.xm就可以实现需求,而不用去修改代码;
对于新增的参数项或者节点开发,我们仅需要开发设备相关的库;
对于新增的事件或者ACS下发的任务,修改library的同时我们只需要稍微修改CWMP core的程序就可以达到目的;
对于新增模块,我们采用线程"插入"的思想来实现,而不用修改程序的主体架构。



