CWMP開源代碼研究5——CWMP程序設計思想


聲明:本文涉及的開源程序代碼學習和研究,嚴禁用於商業目的。 如有任何問題,歡迎和我交流。(企鵝號: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)

  根據配置文件的全路徑和內容初始化數據結構,使用例子如下:

1 char      conf_file[PATH_MAX] = {0};
2 conf_t   *tmp = NULL;
3 
4 tmp = conf_load(conf_file);
5 count = conf_get_int(tmp, "global:count", 0);
View Code

 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文件內容如下:

 1 <TR069 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 2   <trf>
 3     <obj name="InternetGatewayDevice">
 4       <param name="DeviceSummary" getval_func="CpeGetDeviceSummary"></param>
 5       <param name="LANDeviceNumberOfEntries" type="2" getval_func="CpeGetLANDeviceNumberOfEntries"></param>
 6       <param name="WANDeviceNumberOfEntries" type="2" getval_func="CpeGetWANDeviceNumberOfEntries"></param>
 7       <obj name="DeviceInfo" noti_rw="1">
 8         <param name="SpecVersion" getval_func="CpeGetDeviceInfoSpecVersion"/>
 9         <param name="HardwareVersion" getval_func="CpeGetDeviceInfoHardwareVersion"></param>
10         <param name="SoftwareVersion" getval_func="CpeGetDeviceInfoSoftwareVersion"></param>
11         <param name="Manufacturer"      getval_func="CpeGetDeviceInfoManufacturer"></param>
12         <param name="SerialNumber"      getval_func="CpeGetDeviceInfoSerialNumber"></param>
13         <param name="ManufacturerOUI" getval_func="CpeGetDeviceInfoManufacturerOUI"></param>
14         <param name="ProvisioningCode" rw="1" getval_func="CpeGetDeviceInfoProvisioningCode" setval_func="CpeSetDeviceInfoProvisioningCode"></param>
15         <param name="ProductClass" getval_func="CpeGetDeviceInfoProductClass"></param>
16         <param name="DeviceType" getval_func="CpeGetDeviceInfoDeviceType"></param>
17      <param name="ModelName" getval_func="CpeGetDeviceInfoModelName"></param>
18         <param name="CpeWANAddress" noti_rw="1" rw="1" getval_func="CpeGetCpeWANAddress" setval_func="CpeSetCpeWANAddress"/>
19       </obj>
20       <obj name="ManagementServer">
21         <param name="ConnectionRequestURL"                                   getval_func="CpeGetManagementServerConnectionRequestURL"></param>
22         <param name="ConnectionRequestUsername"     rw="1"    noti_rw="1"    getval_func="CpeGetManagementServerConnectionRequestUsername" setval_func="CpeSetManagementServerConnectionRequestUsername"></param>
23         <param name="ConnectionRequestPassword"     rw="1"    noti_rw="1"    getval_func="CpeGetManagementServerConnectionRequestPassword" setval_func="CpeSetManagementServerConnectionRequestPassword"></param>
24         <param name="Username"                      rw="1"    noti_rw="1"    getval_func="CpeGetManagementServerUsername"          setval_func="CpeSetManagementServerUsername"></param>
25         <param name="Password"                      rw="1"    noti_rw="1"    getval_func="CpeGetManagementServerPassword"          setval_func="CpeSetManagementServerPassword"></param>
26         <param name="ParameterKey"                   getval_func="CpeGetManagementServerParameterKey"      setval_func="CpeSetManagementServerParameterKey"/>
27         <param name="URL"                           rw="1"    noti_rw="1"    getval_func="CpeGetManagementServerUrl"               setval_func="CpeSetManagementServerUrl"></param>
28         <param name="PeriodicInformEnable"          rw="1"  noti_rw="1"  type="3" getval_func="CpeGetManagementServerPeriodicInformEnable"      setval_func="CpeSetManagementServerPeriodicInformEnable"></param>
29         <param name="PeriodicInformInterval"        rw="1"  noti_rw="1"  type="2" getval_func="CpeGetManagementServerPeriodicInformInterval"    setval_func="CpeSetManagementServerPeriodicInformInterval"></param>
30         <param name="PeriodicInformTime"            rw="1"          type="4" getval_func="CpeGetManagementServerPeriodicInformTime"        setval_func="CpeSetManagementServerPeriodicInformTime"></param>
31       </obj>
32 
33       <obj name="Time">
34         <param name="Enable"          rw="1"  type="3"     getval_func="CpeGetTimeEnable"          setval_func="CpeSetTimeEnable"></param>
35         <param name="NTPServer1"              rw="1"     getval_func="CpeGetTimeNTPServer1"      setval_func="CpeSetTimeNTPServer1"></param>
36         <param name="CurrentLocalTime"       type="4"             getval_func="CpeGetTimeCurrentLocalTime"></param>
37       </obj>
38       <obj name="X_CT-COM_MonitorCollector">
39          <param name="Enable"      noti_rw="1"     rw="1" type="3"   getval_func="CpeGet_MonitorEnable"                 setval_func="CpeSet_MonitorEnable"></param>
40          <obj name="MonitorConfig"  rw="1" addobj_func="TRF_Add_MonitorConfig" delobj_func="TRF_Del_MonitorConfig"  refresh_func="TRF_Refresh_MonitorConfig">
41            <obj name="0">
42              <param name="ParaList"  noti_rw="1"     rw="1"   getval_func="CpeGet_MonitorConfig_ParaList"                 setval_func="CpeSet_MonitorConfig_ParaList"></param>
43              <param name="TimeList"   rw="1"  type="2"          getval_func="CpeGet_MonitorConfig_TimeList"      setval_func="CpeSet_MonitorConfig_TimeList"></param>
44            </obj>
45         </obj>
46       </obj>    
47       <obj name="LANDevice">
48         <obj name="1">
49           <param name="LANEthernetInterfaceNumberOfEntries" type="2" getval_func="CpeGetLANEthernetInterfaceNumberOfEntries"/>
50         </obj>
51       </obj>
52     <obj name="ObjTest"   rw="1"  addobj_func="TRF_Add_ObjTest" delobj_func="TRF_Del_ObjTest" refresh_func="TRF_Refresh_ObjTest">
53         <obj name="0">
54             <param name="TestEnabled" rw="1" type="3" getval_func="CpeGetObjTest_TestEnabled" setval_func="CpeSetObjTest_TestEnabled"/>
55         </obj>
56     </obj>
57     </obj>
58   </trf>
59 
60   <devlib name="/usr/lib/libcwmp.so"></devlib>
61   
62   <auth name="dev_get_auth"></auth>
63   
64   <listenport name="dev_get_listenport"></listenport>
65   <wanparamname name="dev_get_wanparam_name"></wanparamname>
66   
67   <bootstrap name="dev_bootstrap"></bootstrap>  
68   <init name="dev_init"></init>  
69   <reboot name="dev_reboot"></reboot>
70 
71   <factoryreset name="dev_factoryreset"></factoryreset>
72   <download name="dev_download"></download>
73   <acsstatus name="dev_set_acs_status"></acsstatus>
74   <urldnsresolve name="dev_url_dns_resolve"></urldnsresolve>
75   
76   <upload name="dev_upload"></upload>
77   <cwmpenable name="dev_cwmp_enable"/>
78  
79   <informlist>
80     <inform name="InternetGatewayDevice.DeviceInfo.ModelName"/>
81     <inform name="InternetGatewayDevice.DeviceInfo.DeviceType"/>
82   </informlist>
83 
84   <eventlist>
85     <event name="X CT-COM BIND"></event>
86   </eventlist>
87 
88 
89 </TR069>
View Code

 InternetGatewayDevice是整個參數樹的根。obj表示這是一個對象,obj可以讀,可以寫,當objname0時,表示該obj可以是個模板,為創建后面的實例提供一個模板,當ACS查詢時,不會把obj name0的Obj發送給ACS。Objrw=1,表示該obj可以添加子obj,通過addobj_func來添加,通過delobj_func來刪除,refresh_func表示刷新該obj下的信息。 Objnoti_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消息加入事件隊列。

 1 //診斷
 2 #define TASK_DIAG           1
 3 //重啟
 4 #define TASK_REBOOT         2
 5 //恢復出廠設置
 6 #define TASK_FACTORY        3
 7 //download
 8 #define TASK_DOWNLOAD       4
 9 //upload
10 #define TASK_UPLOAD         5
11 //change ACS URL
12 #define TASK_CHANGE_ACS_URL 6
13 #define TASK_SUBDEVICE      7
14 #define TASK_ADD_EVENT      8
15 #define TASK_ADD_INFORM     9
16 #define TASK_CLEAR_EVENT    10
17 
18 #define TASK_VPN_RESTART    20
19 #define TASK_SYSLOG_RESTART 21
20 #define TASK_FIREWARE_RESTART 22
21 
22 #define TASK_OTHER          99
View Code

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,監視參數變化,記錄事件等。

 1 struct cwmp_context{
 2     file_context_t      file_ctx;           //配置文件
 3     trf_param_t         param_root;         //參數樹根節點
 4     dev_info_t          dev_info;           //設備信息
 5 
 6     void                *handle_lib;        //設備library的handle
 7     
 8     int                 acs_port;           //監聽ACS的端口
 9     int                 acs_retrycount;     //連接ACS重試次數
10     int                 notify_interval;    //監視參數變化的間隔時間
11     
12     pthread_mutex_t     mutex_attr;
13     hash_t              *ht_attr;           //記錄參數屬性,需要上報的。
14 
15     pthread_mutex_t     mutex_val_change;  
16     hash_t              *ht_val_change;     //記錄Value Change
17 
18     char                **inform_array;     //需要上報的參數項數組
19     int                 inform_count;       //需要上報的參數項總數
20 
21     pthread_mutex_t     mutex_inform_tmp;
22     char                **inform_array_tmp; //臨時需要上報的參數項數組
23     int                 inform_count_tmp;   //臨時需要上報的參數項總數
24     
25     pthread_mutex_t     mutex_evt;
26     int                 evt_count;
27     event_info_t        *evt_array;         //記錄事件
28     event_global_t      evt_global_info;    //記錄由於重啟需要保存的信息
29     sem_t               sem_send_acs;       //發送給acs信息的信號量
30     
31     
32     pthread_mutex_t     mutex_task;         //for task_list
33     list_t              *task_list;         //task list
34     sem_t               sem_task;
35 
36     pthread_mutex_t     mutex_param;        //對參數進行加鎖,防止多線程操作時導致程序不穩定
37                                             //僅在對參數有操作的線程中加鎖
38     trans_t             transfer_info;      //用於Download和Upload
39 };
View Code

  2)EventType主要定義了規范中的事件類型

 1 typedef enum
 2 {
 3     EVENT_BOOTSTRAP = 0,
 4     EVENT_BOOT,
 5     EVENT_PERIODIC,
 6     EVENT_SCHEDULED,
 7     EVENT_VALUECHANGE,
 8     EVENT_KICKED,
 9     EVENT_CONNECTIONREQUEST,
10     EVENT_TRANSFERCOMPLETE,
11     EVENT_DIAGNOSTICSCOMPLETE,
12     EVENT_REQUESTDOWNLOAD,
13     EVENT_AUTONOMOUSTRANSFERCOMPLETE,
14     EVENT_MREBOOT,
15     EVENT_MSCHEDULEINFORM,
16     EVENT_MDOWNLOAD,
17     EVENT_MUPLOAD,
18     EVENT_MAXCOUNT
19 }EventType;
View Code

  對於自定義的事件類型可以通過xml中如下定義:

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

  3)

 1 struct trf_param
 2 {
 3     char                name[PARAM_NAME_LEN+1];     //參數名
 4     int                 type;                       //參數類型 trf_datatype_e
 5     int                 writable;                   //是否可寫。0:不可寫,1:可寫,如果object
 6                                                     //可以Add,則可寫
 7     int                 max_instance;               //屬於Object, 最大instance值,-1表示無限制
 8     int                 notification;               //屬於Parameter,  0:off,1:passive,2:active
 9     unsigned char       noti_rw;                    //屬於Parameter,  是否可以設置上報屬性,0 不可以 1 可以
10     unsigned long       acl;                        /*屬於Parameter, access list */
11     TRFGetParamValueFunc    getparamval_func;       //屬於Parameter, 取得參數值函數
12     TRFSetParamValueFunc    setparamval_func;       //屬於Parameter, 設置參數值函數
13     TRFAddObjectFunc        addobject_func;         //屬於Object, AddObject
14     TRFDelObjectFunc        delobject_func;         //屬於Object, DeleteObject
15     TRFRefreshFunc          refresh_func;           //屬於Object, 刷新
16     struct trf_param    *parent;                    //父節點
17     struct trf_param    *child;                     //子節點
18     struct trf_param    *nextSibling;               //兄弟節點
19 };
View Code

  定義樹形結構的節點,每個節點擁有自己的屬性和方法。

  4) 設備相關函數

 1 <devlib name="/usr/lib/libcwmp.so"/>                          Libary的位置
 2 <auth name="dev_get_auth"/>                              是否需要開啟ACS的認證
 3 <listenport name="dev_get_listenport"/>                          CWMP的監聽端口
 4 <wanparamname name="dev_get_wanparam_name"/>                  取得WAN口的參數項名稱的全路徑
 5 <bootstrap name="dev_bootstrap"/>                          判斷是否取得首次連接到ACS的標志
 6 <init name="dev_init"/>                                  初始化設備操作
 7 <reboot name="dev_reboot"/>                              設備的reboot方法
 8 <download name="dev_download"/>                              設備的download的方法,包括下載,升級之類的方法
 9 <upload name="dev_upload"/>                              設備upload的方法,包括生成配置文件,上傳日志等方法
10 <cwmpenable name="dev_cwmp_enable"/>                          判斷是否啟動CWMP進程
11 <urldnsresolve name="dev_url_dns_resolve"></urldnsresolve>    ACS的URL解析
12 ...
13 其他參數項相關的操作函數
View Code

五. 總結

  CWMP core與Datamodel分離,通過不斷的調試和實踐,並應用於不同的運營商(電信,聯通,移動),使得CWMP core的程序不斷成熟和穩定。

  對於相同的功能,我們僅僅需要修改device.xm就可以實現需求,而不用去修改代碼;

  對於新增的參數項或者節點開發,我們僅需要開發設備相關的庫;

  對於新增的事件或者ACS下發的任務,修改library的同時我們只需要稍微修改CWMP core的程序就可以達到目的;

  對於新增模塊,我們采用線程"插入"的思想來實現,而不用修改程序的主體架構。

六. 參考

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

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

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM