|
ONVIF開發(fā)經(jīng)驗總結
一、 利用gsoap2.8.14生成Onvif相關源代碼................................................................ 2 1. 生成onvif.h頭文件的方法................................................................................ 2 2) 通過以下命令生成相關源文件.................................................................. 3 3. 利用gsoap2.8.8生成源代碼和gsoap2.8.14生成的差異.................................... 4 1) typemap.dat文件中需要添加相關信息...................................................... 4 2) wsa5.h中無SOAP_ENV__Fault函數(shù)........................................................... 5
一、 利用gsoap2.8.14生成Onvif相關源代碼 1. 生成onvif.h頭文件的方法 產(chǎn)生頭文件有兩種方法:鏈接網(wǎng)絡生成和本地生成。 1) wsdl2h相關命令參數(shù) -c 產(chǎn)生c語言的代碼,否則產(chǎn)生C++ -s 不使用STL代碼 -t 指定typemap.dat文件 -o 指定生成的頭文件名 l 將wsdl2.exe和typemap.dat文件放入同一個文件夾 l 利用cmd或批處理執(zhí)行以下dos命令: wsdl2h.exe -c -s -t typemap.dat -o onvif.h http://www./onvif/ver10/network/wsdl/remotediscovery.wsdl http://www./onvif/ver10/device/wsdl/devicemgmt.wsdlhttp://www./onvif/ver20/analytics/wsdl/analytics.wsdlhttp://www./onvif/ver10/analyticsdevice.wsdlhttp://www./onvif/ver10/media/wsdl/media.wsdl http://www./onvif/ver10/deviceio.wsdlhttp://www./onvif/ver10/display.wsdlhttp://www./onvif/ver10/event/wsdl/event.wsdlhttp://www./onvif/ver20/imaging/wsdl/imaging.wsdlhttp://www./onvif/ver10/recording.wsdl http://www./onvif/ver10/replay.wsdlhttp://www./onvif/ver10/search.wsdlhttp://www./onvif/ver10/receiver.wsdlhttp://www./onvif/ver20/ptz/wsdl/ptz.wsdl l 從ONVIF官網(wǎng)上把相關的WSDL文檔下載到本地,下載地址(詳細參見備注說明),此外還需下載樣式表,下載地址見WSDL文檔中的schemaLocation。 l 將wsdl2.exe、typemap.dath、WSDL文檔和樣式表放入同一文件夾中。 l 將相關WSDL文檔中的樣式表引入路徑(schemaLocation)修改為本地實際地址,如:event.wsdl中schemaLocation="http://www./2005/08/addressing/ws-addr.xsd修改為schemaLocation = ws-addr.xsd l 利用cmd或批處理執(zhí)行以下命令 wsdl2h.exe -c -s -t typemap.dat -o onvif.hremotediscovery.wsdldevicemgmt.wsdl analytics.wsdl analyticsdevice.wsdl media.wsdl deviceio.wsdldisplay.wsdl event.wsdl imaging.wsdl ecording.wsdl replay.wsdl search.wsdlreceiver.wsdl ptz.wsdl l wsdl2.exe位于gsoap_2.8.14\gsoap-2.8\gsoap\bin l typemap.dat位于gsoap_2.8.14\gsoap-2.8\gsoap l wsdl下載地址:http://www./Documents/Specifications.aspx l 各url之間用空格隔開 l typemap.dat不需要修改 l 鏈接網(wǎng)絡生成方法的優(yōu)點是不用考慮以上文件對其它文件的依賴關系,不用修改引入路徑。該方法的缺點跟網(wǎng)速有關,因此中途可能會中斷,如果超過5分鐘未生成,可重新執(zhí)行命令, 通過代理上網(wǎng)則無法生成,不推薦用此方法。 l 下載本地生成方法的優(yōu)缺點和鏈接網(wǎng)絡方法生成相反,代理網(wǎng)推薦使用此方法。 2. 生成onvif源代碼 把剛生成的onvif.h與soapcpp2.exe、import和custom放入同一文件夾,其中: soapcpp2.exe位于gsoap_2.8.14\gsoap-2.8\gsoap\bin import位于gsoap_2.8.14\gsoap-2.8\gsoap custom位于gsoap_2.8.14\gsoap-2.8\gsoap soapcpp2.exe-2 –c onvif.h -I import 3) Soapcpp2相關命令參數(shù) -2 采用SOAP1.2, -x 不產(chǎn)生xml文件(不推薦使用此命令,因為xml文檔很有用) -I 為引入路徑 -C 只產(chǎn)生客戶端代碼(注意:C是大寫) l 需要在onvif.h中加入#import"wsse.h,用來做安全驗證 l 需要將import目錄下的wsa5.h以下部分注釋掉,否則編譯時會報soap_xxxx_SOAP_ENV__Fault()函數(shù)重復定義。 int SOAP_ENV__Fault ( _QName faultcode, // SOAP 1.1 char *faultstring, //SOAP 1.1 char *faultactor, //SOAP 1.1 struct SOAP_ENV__Detail *detail, // SOAP 1.1 struct SOAP_ENV__Code *SOAP_ENV__Code, // SOAP1.2 struct SOAP_ENV__Reason *SOAP_ENV__Reason, // SOAP 1.2 char *SOAP_ENV__Node, // SOAP 1.2 char *SOAP_ENV__Role, // SOAP 1.2 struct SOAP_ENV__Detail *SOAP_ENV__Detail, // SOAP 1.2 void);
3. 利用gsoap2.8.8生成源代碼和gsoap2.8.14生成的差異 利用gsoap2.8.8生成源代碼方法跟gsoap2.8.14基本一致,但需注意以下區(qū)別: 1) typemap.dat文件中需要添加相關信息 tds ="http://www./ver10/device/wsdl" tev ="http://www./ver10/events/wsdl" tls ="http://www./ver10/display/wsdl" tmd ="http://www./ver10/deviceIO/wsdl" timg ="http://www./ver20/imaging/wsdl" trt ="http://www./ver10/media/wsdl" tptz ="http://www./ver20/ptz/wsdl" trv ="http://www./ver10/receiver/wsdl" trc ="http://www./ver10/recording/wsdl" tse ="http://www./ver10/search/wsdl" trp ="http://www./ver10/replay/wsdl" tan ="http://www./ver20/analytics/wsdl" tad ="http://www./ver10/analyticsdevice/wsdl" tdn ="http://www./ver10/network/wsdl" tt ="http://www./ver10/schema"
# OASISrecommended prefixes wsnt ="http://docs./wsn/b-2" wsntw ="http://docs./wsn/bw-2" wsrfbf ="http://docs./wsrf/bf-2" wsrfr ="http://docs./wsrf/r-2" wsrfrw = "http://docs./wsrf/rw-2" wstop ="http://docs./wsn/t-1"
# WS-Discovery 1.0 remapping wsdd10__HelloType = | wsdd__HelloType wsdd10__ByeType = | wsdd__ByeType wsdd10__ProbeType = | wsdd__ProbeType wsdd10__ProbeMatchesType = | wsdd__ProbeMatchesType wsdd10__ProbeMatchType = | wsdd__ProbeMatchType wsdd10__ResolveType = | wsdd__ResolveType wsdd10__ResolveMatchesType = | wsdd__ResolveMatchesType wsdd10__ResolveMatchType = | wsdd__ResolveMatchType # SOAP-ENV mapping SOAP_ENV__Envelope = struct SOAP_ENV__Envelope { struct SOAP_ENV__Header*SOAP_ENV__Header; _XML SOAP_ENV__Body; }; | struct SOAP_ENV__Envelope SOAP_ENV__Header = | struct SOAP_ENV__Header SOAP_ENV__Fault = | struct SOAP_ENV__Fault SOAP_ENV__Detail = | struct SOAP_ENV__Detail SOAP_ENV__Code = |struct SOAP_ENV__Code SOAP_ENV__Subcode = | struct SOAP_ENV__Subcode SOAP_ENV__Reason = | struct SOAP_ENV__Reason 2) wsa5.h中無SOAP_ENV__Fault函數(shù) 由于wsa5.h中沒有SOAP_ENV__Fault因此不會產(chǎn)生代碼重復,因此不用注釋。
二、新建工程,調(diào)試代碼 1. 新建一個項目 將上面生成的soapH.h、soapStub.h、 wsdd.nsmap、soapC.c、soapClient.c,還有位于gsoap-2.8\gsoap 的:stdsoap2.c、stdsoap2.h和位于\custom中的:duration.c放入工程中,然后編寫main函數(shù),調(diào)試代碼。 2. 文件主要功能說明 wsdd.nsmap 名空間定義,服務器端與客戶端都要包含它,里面有很多,都是 相同的,只需導入一個進入工程就行 stdsoap2.h Header _le of stdsoap2.cppruntime library stdsoap2.c RuntimeC library with XML parser and run-time support routines soapStub.h soapH.h //Main header file to be included by all client and servicesources soapC.c //Serializers and deserializers for the specfied datastructures soapClient.c //Clientstub routines for remote operations soapStub.h Amodi_ed and annotated header file produced from the input header file
三、設備發(fā)現(xiàn)main函數(shù)說明 #include <iostream> #include "wsdd.nsmap" #include "soapH.h" using namespace std; int main() { /*****聲明變量***********/ structsoap *soap; //soap環(huán)境變量 structwsdd__ProbeType req; //客戶端發(fā)送的Probe struct__wsdd__ProbeMatches resp; //服務端回的Probematchs structwsdd__ScopesType sScope; //Probe里面的范圍 structSOAP_ENV__Header header; //SOAP的頭 intresult = 0; //返回值 int count = 0; //獲得的設信息備個數(shù)
/**獲取guid(windows下叫guid,linux下叫uuid),格式為urn:uuid:8-4-4-4-12,由系統(tǒng)隨機產(chǎn)生**/ staticchar buf[64] = {0}; //用來保存guid號
GUID guid; /*聲明guid為GUID結構體變量,包含4個變量,分別是 unsigned longData1; unsigned short Data2; unsigned short Data3; unsigned char Data4[ 8 ]; */ if (S_OK== CoCreateGuid(&guid)) //如果guid生成成功,則將其轉為字符串,保存在buf中 { _snprintf(buf,sizeof(buf) ,"urn:uuid:%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X" , guid.Data1 , guid.Data2 , guid.Data3 , guid.Data4[0], guid.Data4[1] , guid.Data4[2], guid.Data4[3], guid.Data4[4],guid.Data4[5] , guid.Data4[6], guid.Data4[7] ); } soap = soap_new(); //初始化soap if(soap==NULL) { return -1; } soap_set_namespaces(soap,namespaces); //設置命名空間 soap->recv_timeout = 5; //設置接收Probematchs時間,超過5秒鐘沒有數(shù)據(jù)就退出 soap_default_SOAP_ENV__Header(soap,&header); //將header設置為soap消息的頭屬性 /*****給頭賦值******/ header.wsa__MessageID =buf; header.wsa__To="urn:schemas-xmlsoap-org:ws:2005:04:discovery"; header.wsa__Action="http://schemas./ws/2005/04/discovery/Probe"; soap->header = &header; /*設置所需尋找設備的類型和范圍,二者至少設定一個,否則可能收到非ONVIF設備,出現(xiàn)異常*/ soap_default_wsdd__ScopesType(soap,&sScope); sScope.__item ="onvif://www."; //設置所需設備的sScope soap_default_wsdd__ProbeType(soap,&req); req.Scopes = &sScope; req.Types ="tdn:NetworkVideoTransmitter"; /*設置所需設備的類型,tdn為命名空間前綴,為wsdd.nsmap文件中{"tdn","http://www./ver10/network/wsdl"}的tdn,如過不是tdn,而是其它,如ns1這里也要隨之改為ns1*/
//通過組播發(fā)送Probe探針,發(fā)送成功返回0,否則-1 result = soap_send___wsdd__Probe(soap,"soap.udp://239.255.255.250:3702", NULL, &req); if(result==-1) { cout<<"soap error:"<<soap->error<<soap_faultcode(soap) <<"---"<<soap_faultstring(soap)<<endl; }else { do{ result = soap_recv___wsdd__ProbeMatches(soap,&resp); //接收ProbeMatches,成功返回0,否則-1 if (result==-1) { cout<<"共發(fā)現(xiàn)"<<count<<"個設備"<<endl; cout<<"soap error:"<<soap->error<<soap_faultcode(soap) <<"---"<<soap_faultstring(soap)<<endl; break; }else { count++; cout<<"========================================="<<endl; cout<<"UUID:"<<""<<resp.wsdd__ProbeMatches->ProbeMatch-> wsa__EndpointReference.Address<<endl; cout<<"Type:"<<""<<resp.wsdd__ProbeMatches->ProbeMatch->Types<<endl; cout<<"Scopes:"<<""<< resp.wsdd__ProbeMatches-> ProbeMatch->Scopes->__item<<endl; cout<<"DeviceService Address:"<<""<<resp.wsdd__ProbeMatches-> ProbeMatch->XAddrs<<endl; cout<<"MetadataVersion:"<<""<<resp.wsdd__ProbeMatches-> ProbeMatch->MetadataVersion<<endl; } }while(1); } /********清除變量************/ soap_destroy(soap); // removedeserialized class instances (C++ only) soap_end(soap); //clean up and remove deserialized data soap_done(soap); returnresult; } 1. 出現(xiàn)如下語法錯誤: error C2143:語法錯誤 : 缺少“{”(在“:”的前面) error C2059:語法錯誤 : “:” error C2143:語法錯誤 : 缺少“{”(在“:”的前面) 需要將工程中的.c文件改成.cpp文件即可。 2. 無法解析的外部命令錯誤soap_check_faultsubcode 在stdsoap2.h中聲明的soap_check_faultsubcode(structsoap *soap)函數(shù)在soapC.cpp中未實現(xiàn), 可在soapC.cpp中添加如下實現(xiàn): SOAP_FMAC3 const char * SOAP_FMAC4soap_check_faultsubcode(struct soap *soap) { soap_fault(soap); if(soap->version == 2) { if(soap->fault->SOAP_ENV__Code &&soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode &&soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode) returnsoap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode->SOAP_ENV__Value; return NULL; } returnsoap->fault->faultcode; } 3. 無法解析的外部命令錯誤soap_check_faultdetail 在stdsoap2.h中聲明的soap_check_faultdetail(struct soap *soap)函數(shù)在soapC.cpp中未實現(xiàn), 可在soapC.cpp中添加如下實現(xiàn): SOAP_FMAC3 const char * SOAP_FMAC4soap_check_faultdetail(struct soap *soap) { soap_fault(soap); if(soap->version == 2 && soap->fault->SOAP_ENV__Detail) returnsoap->fault->SOAP_ENV__Detail->__any; if(soap->fault->detail) return soap->fault->detail->__any; returnNULL; } 4. 出現(xiàn)無法解析的外部符號_soap_in_xsd__duration 無法解析的外部符號_soap_in_xsd__duration,該符號在函數(shù)_soap_getelement中被引用soapC.obj : error LNK2019: 無法解析的外部符號_soap_out_xsd__duration,該符號在函數(shù)_soap_putelement中被引用 soapC.obj: error LNK2019: 無法解析的外部符號_soap_default_xsd__duration,該符號在函數(shù)_soap_default__tse__FindMetadata中被引用 需要將\custom文件夾下面的duration.h和duration.c導入工程中。 5. 在VS中出現(xiàn)fatal error C1128: 節(jié)數(shù)超過對象文件格式限制:請使用/bigobj 進行編譯的錯誤 這是由于源代碼文件太大的原因,需添加選項/bigobj,在項目屬性-> C/C++ ->命令行的附加選項中添加/bigobj。 6. 如果是調(diào)用soap_call_XXXX_Probe()來實現(xiàn)設備發(fā)現(xiàn)時不能發(fā)現(xiàn)所有onvif設備 該函數(shù)實現(xiàn)過程中只有一次接收過程,所以無法發(fā)現(xiàn)所有的設備的問題。如果使用該函數(shù),還需要對函數(shù)的實現(xiàn)做以下更改: 函數(shù)的接收部分,將原來的XXXX:Response該為YYYY:ProbeMatches, 其中XXXX是.nsmap文件中http://www./ver10/network/wsdl"所對應的命名空間前綴,YYYY與后面YYYY:ProbeMatchesType中的前綴相同,都是http://schemas./ws/2005/04/discovery所對應的命名空間前綴名。 7. 抓包實驗問題 利用gsoap生成的wsdd.namsp如下: #include "soapH.h" SOAP_NMAC struct Namespace namespaces[] = { {"SOAP-ENV","http://schemas./soap/envelope/","http://www./*/soap-envelope", NULL}, {"SOAP-ENC","http://schemas./soap/encoding/","http://www./*/soap-encoding", NULL}, {"xsi","http://www./2001/XMLSchema-instance","http://www./*/XMLSchema-instance", NULL}, {"xsd","http://www./2001/XMLSchema","http://www./*/XMLSchema", NULL}, {"wsa","http://schemas./ws/2004/08/addressing", NULL, NULL}, {"wsdd","http://schemas./ws/2005/04/discovery", NULL, NULL}, {"chan","http://schemas.microsoft.com/ws/2005/02/duplex", NULL, NULL}, {"wsa5","http://www./2005/08/addressing","http://schemas./ws/2004/08/addressing", NULL}, {"xmime","http:///xmime.xsd", NULL, NULL}, {"xop","http://www./2004/08/xop/include", NULL, NULL}, {"tt","http://www./ver10/schema", NULL, NULL}, {"wsrfbf","http://docs./wsrf/bf-2", NULL, NULL}, {"wstop","http://docs./wsn/t-1", NULL, NULL}, {"wsrfr","http://docs./wsrf/r-2", NULL, NULL}, {"tad","http://www./ver10/analyticsdevice/wsdl", NULL, NULL}, {"tan","http://www./ver20/analytics/wsdl", NULL, NULL}, {"tdn","http://www./ver10/network/wsdl", NULL, NULL}, {"tds","http://www./ver10/device/wsdl", NULL, NULL}, {"tev","http://www./ver10/events/wsdl", NULL, NULL}, {"wsnt","http://docs./wsn/b-2", NULL, NULL}, {"timg","http://www./ver20/imaging/wsdl", NULL, NULL}, {"tls","http://www./ver10/display/wsdl", NULL, NULL}, {"tmd","http://www./ver10/deviceIO/wsdl", NULL, NULL}, {"tptz","http://www./ver20/ptz/wsdl", NULL, NULL}, {"trc","http://www./ver10/recording/wsdl", NULL, NULL}, {"trp","http://www./ver10/replay/wsdl", NULL, NULL}, {"trt","http://www./ver10/media/wsdl", NULL, NULL}, {"trv","http://www./ver10/receiver/wsdl", NULL, NULL}, {"tse","http://www./ver10/search/wsdl", NULL, NULL}, {NULL,NULL, NULL, NULL} };
1)通過編寫面函數(shù)之后,調(diào)試運行結果如下:
其中http://192.168.106.112:80/onvif/device_service為本地模擬的設備
通過抓包工具獲得信息如下:
2).保留以下命名空間,刪除其它信息: #include "soapH.h" SOAP_NMAC struct Namespace namespaces[] = { {"SOAP-ENV","http://schemas./soap/envelope/","http://www./*/soap-envelope", NULL}, {"SOAP-ENC","http://schemas./soap/encoding/","http://www./*/soap-encoding", NULL}, {"xsi","http://www./2001/XMLSchema-instance","http://www./*/XMLSchema-instance", NULL}, {"xsd","http://www./2001/XMLSchema","http://www./*/XMLSchema", NULL}, {"wsa","http://schemas./ws/2004/08/addressing", NULL, NULL}, {"wsdd","http://schemas./ws/2005/04/discovery", NULL, NULL}, {"wsa5","http://www./2005/08/addressing","http://schemas./ws/2004/08/addressing", NULL}, {"xmime","http:///xmime.xsd", NULL, NULL}, {"xop","http://www./2004/08/xop/include", NULL, NULL}, {"tt","http://www./ver10/schema", NULL, NULL}, {NULL,NULL, NULL, NULL} }; 調(diào)試運行結果如下:
抓包工具抓到信息如下:
3)測試工具信息如下 抓包信息如下:
4)多次實驗顯示: wsdd.namsp文件太大時,將會被拆包,192.168.106.164將始終不會回消息,減小命名空間大小,保證只發(fā)送一個UDP包,192.168.106.164將始終能回消息
5) 通過抓包發(fā)現(xiàn),做設備管理功能時客戶端已能構與設備端通信,只是涉及到安全,設備端沒有返回信息
6)在程序運行時,可能會出現(xiàn)一下信息 這是由于Type和Scopes都沒有賦值,接收了非ONVIF的設備 它的body中沒有我們需要的值,所以在輸出時會引起中斷
1. 對于利用gsoap工具實現(xiàn)基于ONVIF標準的功能,盡量按照如下順序: 了解所需實現(xiàn)的功能原理,參考<<ONVIF_Core_Specification,_version_2.0.pdf>> 了解gsoap工具的使用方法和編程方法,還有文件結構,參考<<gSOAP 2.8.14 User Guide>>,位于gsoap_2.8.14\gsoap-2.8\gsoap\doc\soapdoc2.pdf里面,里面內(nèi)容很多,可根據(jù)需要查找相關內(nèi)容,如The wsdl2h WSDL and Schema Importer(84), Using thesoapcpp2 Compiler and Code Generator(89)SOAP Header Processing(178頁),SOAP/XML Over UDP(208頁) 根據(jù)所需實現(xiàn)的功能查看對應的xml文檔(生成源代碼時產(chǎn)生的,)因為生成的xml文檔是客戶端和服務端通信時所發(fā)送的模板結構,通過它可以了解編碼時所需要填充的信息 了解工程中.h和.cpp的功能 查看別人寫的例子,理解原理,要學會從原理上去分析碰到的問題并解決問題 自己寫代碼驗證,事實求是,替自己負責,替用戶負責。 2. 善于利用抓包工具 從原理上分析問題,能大大提高效率,如果利用測試工具跟客戶端通信,然后進行抓包,能構很好的分析出客戶端需發(fā)送的消息,服務端回的消息,非常利于編碼。 |
|
|