转载地址:http://blog.csdn.net/gubenpeiyuan/article/details/25618177

概要:

          目前ONVIF协议家族设备已占据数字监控行业半壁江山以上,亲,作为开发者的你还在犹豫是否了解下吗?本文介绍了ONVIF客户端从设备搜索,鉴权,能力获取,媒体信息获取,URI地址获取的整套流程。文章只讲述了比较重要或其他博文没有讲述的开发点,详细可以参考文末参考文章。最后,能获得rtsp地址之后,然后去做其他功能比如录像,ptz这些就非常得心应手了。本文出自CSDN-固本培元 ,转载注明出自:leolupy@gmail.com。

前言及鸣谢:

感谢guog先生,快活林高先生,onvif全国交流群的的酷夏先生在开发过程中给予的巨大支持,没有你们的帮助开发过程将异常艰难啊。谢谢了!

ONVIF介绍:

搜索:Probe: 发现网络摄像头,获取webserver地址

http://192.168.15.240/onvif/device_service

能力获取:GetCapabilities:获取设备能力文件,从中识别出媒体信息地址URI:  http://192.168.15.240/onvif/Media

媒体信息获取:GetProfiles: 获取媒体信息文件,识别主通道、子通道的视频编码分辨率

RTSP地址获取:GetStreamUri:获取指定通道的流媒体地址  rtsp://192.168.15.240:554/Streaming/Channels/2?transportmode=unicast

Gsoap及开发框架生成:

离线文件下载地址:感谢guog先生的共享:

命令:

[plain] view plaincopy
  1. wsdl2h -o onvif.h -c -s -t ./typemap.dat devicemgmt.wsdl media.wsdl event.wsdl display.wsdl deviceio.wsdl imaging.wsdl ptz.wsdl receiver.wsdl recording.wsdl search.wsdl remotediscovery.wsdl replay.wsdl analytics.wsdl analyticsdevice.wsdl actionengine.wsdl accesscontrol.wsdl doorcontrol.wsdl

离线文件在:

http://download.csdn.net/detail/u011597695/5875143

    在线命令:
[plain] view plaincopy
  1. wsdl2h -o onvif.h -c -s -t ./typemap.dat http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl http://www.onvif.org/onvif/ver10/event/wsdl/event.wsdl http://www.onvif.org/onvif/ver10/display.wsdl http://www.onvif.org/onvif/ver10/deviceio.wsdl http://www.onvif.org/onvif/ver20/imaging/wsdl/imaging.wsdl http://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl http://www.onvif.org/onvif/ver10/receiver.wsdl  http://www.onvif.org/onvif/ver10/recording.wsdl http://www.onvif.org/onvif/ver10/search.wsdl http://www.onvif.org/onvif/ver10/network/wsdl/remotediscovery.wsdl http://www.onvif.org/onvif/ver10/replay.wsdl http://www.onvif.org/onvif/ver20/analytics/wsdl/analytics.wsdl http://www.onvif.org/onvif/ver10/analyticsdevice.wsdl http://www.onvif.org/ver10/actionengine.wsdl http://www.onvif.org/ver10/pacs/accesscontrol.wsdl http://www.onvif.org/ver10/pacs/doorcontrol.wsdl

(记得拷贝gsoap的typemap文件至生成目录下,wsdl2h命令需要这个。)

     离线命令:
[plain] view plaincopy
  1. wsdl2h -o onvif.h -c -s -t ./typemap.dat devicemgmt.wsdl media.wsdl event.wsdl display.wsdl deviceio.wsdl imaging.wsdl ptz.wsdl receiver.wsdl recording.wsdl search.wsdl remotediscovery.wsdl replay.wsdl analytics.wsdl analyticsdevice.wsdl actionengine.wsdl accesscontrol.wsdl doorcontrol.wsdl

现在可以开始生成了:如下:

如果直接生成对应C的库文件会发生重复定义错误,可以修改该文件。

wsa5.h(288): **ERROR**: remote method name clash: struct/class 'SOAP_ENV__Fault' already declared at line 274

打开文件gsoap_2.8.16/gsoap-2.8/gsoap/import/ wsa5.h

将277行int SOAP_ENV__Fault修改为int SOAP_ENV__Fault_alex

笔者没有使用这种方法,是将这个结构体直接注释的方式,最后的结果是,都可以使用。

同时上一步生成的onvif.h文件中没有打开wsse.h, 导致最后生成代码中SOAP_ENV__Header 结构体中缺少定义 wsse__Security数据段,无法进行鉴权命令。

即:添加对openssl的支持,在上一步生成的onvif.h中添加(可选)

[cpp] view plaincopy
  1. #import "wsse.h"

随后使用命令生成:

[plain] view plaincopy
  1. soapcpp2  -c onvif.h -x -I/root/Tools/Gsoap/gsoap-2.8/gsoap/import -I/root/Tools/Gsoap/gsoap-2.8/gsoap/ -I/root/Tools/Gsoap/gsoap-2.8/gsoap/custom -I/root/Tools/Gsoap/gsoap-2.8/gsoap/extras -I/root/Tools/Gsoap/gsoap-2.8/gsoap/plugin

到此为止,基于 C 的客户端和服务器的Onvif开发框架及已经搭建完成。

设备搜索原理及编程技巧:

[plain] view plaincopy
  1. struct soap* NewSoap(struct SOAP_ENV__Header *header,struct soap* soap,
  2. wsdd__ProbeType *req_,
  3. wsdd__ScopesType *sScope_)
  4. {
  5. soap = soap_new();
  6. if(NULL == soap )
  7. {
  8. printf("sopa new error\r\n");
  9. return NULL;
  10. }
  11. soap->recv_timeout = 5;
  12. soap_set_namespaces(soap, namespaces);
  13. soap_default_SOAP_ENV__Header(soap, header);
  14. uuid_t uuid;
  15. char guid_string[100];
  16. uuid_generate(uuid);
  17. uuid_unparse(uuid, guid_string);
  18. header->wsa__MessageID = guid_string;
  19. header->wsa__To = "urn:schemas-xmlsoap-org:ws:2005:04:discovery";
  20. header->wsa__Action = "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe";
  21. soap->header = header;
  22. soap_default_wsdd__ScopesType(soap, sScope_);
  23. sScope_->__item = "";
  24. soap_default_wsdd__ProbeType(soap, req_);
  25. req_->Scopes = sScope_;
  26. req_->Types = ""; //"dn:NetworkVideoTransmitter";
  27. return soap ;
  28. }
[plain] view plaincopy
  1. int i = 0;
  2. result = soap_send___wsdd__Probe(soap, MULTICAST_ADDRESS, NULL, &req);
  3. while(result == SOAP_OK)
  4. {
  5. result = soap_recv___wsdd__ProbeMatches(soap, &resp);
  6. if(result == SOAP_OK)
  7. {
  8. if(soap->error)
  9. {
  10. printf("soap error 1: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
  11. result = soap->error;
  12. }
  13. else
  14. {
  15. printf("Onvif Device detected *********************************************\r\n");
  16. for(i = 0; i < resp.wsdd__ProbeMatches->__sizeProbeMatch; i++)
  17. {
  18. printf("__sizeProbeMatch        : %d\r\n", resp.wsdd__ProbeMatches->__sizeProbeMatch);
  19. printf("wsa__EndpointReference       : %p\r\n", resp.wsdd__ProbeMatches->ProbeMatch->wsa__EndpointReference);
  20. printf("Target EP Address       : %s\r\n", resp.wsdd__ProbeMatches->ProbeMatch->wsa__EndpointReference.Address);
  21. printf("Target Type             : %s\r\n", resp.wsdd__ProbeMatches->ProbeMatch->Types);
  22. printf("Target Service Address  : %s\r\n", resp.wsdd__ProbeMatches->ProbeMatch->XAddrs);
  23. printf("Target Metadata Version : %d\r\n", resp.wsdd__ProbeMatches->ProbeMatch->MetadataVersion);
  24. if(resp.wsdd__ProbeMatches->ProbeMatch->Scopes)
  25. {
  26. printf("Target Scopes Address   : %s\r\n", resp.wsdd__ProbeMatches->ProbeMatch->Scopes->__item);
  27. }
  28. }
  29. break;
  30. }
  31. }
  32. else if (soap->error)
  33. {
  34. printf("[%d] soap error 2: %d, %s, %s\n", __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
  35. result = soap->error;
  36. }
  37. }
        注:搜索到的设备可以加入到自己的设备管理中,这里就不做过多的说明了。

设备鉴权:

[plain] view plaincopy
  1. soap_wsse_add_UsernameTokenDigest(soap,"user", ONVIF_USER, ONVIF_PASSWORD);

原理也很容易明白其实,就是讲http的soap消息加入对应header中xml的元素而已,然后敏感消息digest MD5加密编码。

         所以编译过程中需要使用 lcrypto 也就很正常了。

获取能力:

[plain] view plaincopy
  1. void UserGetCapabilities(struct soap *soap  ,struct __wsdd__ProbeMatches *resp,
  2. struct _tds__GetCapabilities *capa_req,struct _tds__GetCapabilitiesResponse *capa_resp)
  3. {
  4. capa_req->Category = (enum tt__CapabilityCategory *)soap_malloc(soap, sizeof(int));
  5. capa_req->__sizeCategory = 1;
  6. *(capa_req->Category) = (enum tt__CapabilityCategory)(tt__CapabilityCategory__Media);
  7. capa_resp->Capabilities = (struct tt__Capabilities*)soap_malloc(soap,sizeof(struct tt__Capabilities)) ;
  8. soap_wsse_add_UsernameTokenDigest(soap,"user", ONVIF_USER, ONVIF_PASSWORD);
  9. printf("\n--------------------Now Gettting Capabilities NOW --------------------\n\n");
  10. int result = soap_call___tds__GetCapabilities(soap, resp->wsdd__ProbeMatches->ProbeMatch->XAddrs, NULL, capa_req, capa_resp);
  11. if (soap->error)
  12. {
  13. printf("[%s][%d]--->>> soap error: %d, %s, %s\n", __func__, __LINE__, soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
  14. int retval = soap->error;
  15. exit(-1) ;
  16. }
  17. else
  18. {
  19. printf(" \n--------------------GetCapabilities  OK! result=%d--------------\n \n",result);
  20. if(capa_resp->Capabilities==NULL)
  21. {
  22. printf(" GetCapabilities  failed!  result=%d \n",result);
  23. }
  24. else
  25. {
  26. printf(" Media->XAddr=%s \n", capa_resp->Capabilities->Media->XAddr);
  27. }
  28. }
  29. }

获取媒体信息Profile:

[plain] view plaincopy
  1. void UserGetProfiles(struct soap *soap,struct _trt__GetProfiles *trt__GetProfiles,
  2. struct _trt__GetProfilesResponse *trt__GetProfilesResponse ,struct _tds__GetCapabilitiesResponse *capa_resp)
  3. {
  4. int result=0 ;
  5. printf("\n-------------------Getting Onvif Devices Profiles--------------\n\n");
  6. soap_wsse_add_UsernameTokenDigest(soap,"user", ONVIF_USER, ONVIF_PASSWORD);
  7. result = soap_call___trt__GetProfiles(soap, capa_resp->Capabilities->Media->XAddr, NULL, trt__GetProfiles, trt__GetProfilesResponse);
  8. if (result==-1)
  9. //NOTE: it may be regular if result isn't SOAP_OK.Because some attributes aren't supported by server.
  10. //any question email leoluopy@gmail.com
  11. {
  12. printf("soap error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
  13. result = soap->error;
  14. exit(-1);
  15. }
  16. else{
  17. printf("\n-------------------Profiles Get OK--------------\n\n");
  18. if(trt__GetProfilesResponse->Profiles!=NULL)
  19. {
  20. if(trt__GetProfilesResponse->Profiles->Name!=NULL){
  21. printf("Profiles Name:%s  \n",trt__GetProfilesResponse->Profiles->Name);
  22. }
  23. if(trt__GetProfilesResponse->Profiles->token!=NULL){
  24. printf("Profiles Taken:%s\n",trt__GetProfilesResponse->Profiles->token);
  25. }
  26. }
  27. else{
  28. printf("Profiles Get inner Error\n");
  29. }
  30. }
  31. printf("Profiles Get Procedure over\n");
  32. }

获取RTSP的URI:

[plain] view plaincopy
  1. void UserGetUri(struct soap *soap,struct _trt__GetStreamUri *trt__GetStreamUri,struct _trt__GetStreamUriResponse *trt__GetStreamUriResponse,
  2. struct _trt__GetProfilesResponse *trt__GetProfilesResponse,struct _tds__GetCapabilitiesResponse *capa_resp)
  3. {
  4. int result=0 ;
  5. trt__GetStreamUri->StreamSetup = (struct tt__StreamSetup*)soap_malloc(soap,sizeof(struct tt__StreamSetup));//初始化,分配空间
  6. trt__GetStreamUri->StreamSetup->Stream = 0;//stream type
  7. trt__GetStreamUri->StreamSetup->Transport = (struct tt__Transport *)soap_malloc(soap, sizeof(struct tt__Transport));//初始化,分配空间
  8. trt__GetStreamUri->StreamSetup->Transport->Protocol = 0;
  9. trt__GetStreamUri->StreamSetup->Transport->Tunnel = 0;
  10. trt__GetStreamUri->StreamSetup->__size = 1;
  11. trt__GetStreamUri->StreamSetup->__any = NULL;
  12. trt__GetStreamUri->StreamSetup->__anyAttribute =NULL;
  13. trt__GetStreamUri->ProfileToken = trt__GetProfilesResponse->Profiles->token ;
  14. printf("\n\n---------------Getting Uri----------------\n\n");
  15. soap_wsse_add_UsernameTokenDigest(soap,"user", ONVIF_USER, ONVIF_PASSWORD);
  16. soap_call___trt__GetStreamUri(soap, capa_resp->Capabilities->Media->XAddr, NULL, trt__GetStreamUri, trt__GetStreamUriResponse);
  17. if (soap->error) {
  18. printf("soap error: %d, %s, %s\n", soap->error, *soap_faultcode(soap), *soap_faultstring(soap));
  19. result = soap->error;
  20. }
  21. else{
  22. printf("!!!!NOTE: RTSP Addr Get Done is :%s \n",trt__GetStreamUriResponse->MediaUri->Uri);
  23. }
  24. }
最后贴一个终端截图:

开发注意事项:(必读)

soap通信的命名空间如果错误则不能检索到设备:编译好的wsdd.nsmap文件需要修改命名空间,如下:

如果要正常开发,被检索到,或者发现其他设备需要nsmap修改如下:1.1换1.2

[plain] view plaincopy
  1. 以下命名空间表示SOAP1.1版本:
  2. {"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", "http://www.w3.org/*/soap-envelope", NULL},
  3. {"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", "http://www.w3.org/*/soap-encoding", NULL}, //1.1
  4. 以下命名空间表示SOAP1.2版本:
  5. {"SOAP-ENV", "http://www.w3.org/2003/05/soap-envelope", "http://schemas.xmlsoap.org/soap/envelope/", NULL},
  6. {"SOAP-ENC", "http://www.w3.org/2003/05/soap-encoding", "http://schemas.xmlsoap.org/soap/encoding/", NULL},  //1.2

另外存在的客户端搜索不到设备情况:

1.是否有vpn,存在的话,本机IP会产生变化导致不能搜到?抓包可以看到,3702端口包的数据源地址改变。

2.uuid是否已经赋值。

3.有时,windows宿主机装有虚拟机,也可能造成onvif客户端的ip获取错误。故搜索不到。

这些问题,在交换机或者路由支持本地局域网跨网段数据UDP交互时,均不会产生。

调试技巧:

fsend/ frecv 打印出发送和接收到的报文。使用xml编辑器分析。当然也可以直接用浏览器看。

1、打开onvif调试开关,以便让onvif打印一些可用的调试信息。

在Makefile中添加调试宏定义如: CC = gcc -DDEBUG

2、打开调试宏后,默认在程序运行的目录产生三个文件:

RECV.log

SENT.log

TEST.log

RECV.log是onvif接收到的SOAP数据,没接收一条,都会在RECV.log中记录

SENT.log是onvif发送出去的SOAP数据,没发送一套,也会在SENT.log中生成记录

最后是TEST.log,如果说RECV和SENT可以用wireshark工具抓包代替,那么TEST.log是谁也替代不了的,TEST.log记录了onvif的实时的工作状态。

尤其当出现segmentation fault错误,TEST.log就成了唯一一个能够定位到具体内存出错的地方了。

SOAP_TYPE返回soap->error=4的错误说明

关于数据正确(抓包可收到数据),但soap返回错误,为4 及 SOAP_TYPE 的问题:

GetCapabilities的过程错误时。

多次调试后得出结论,是tt__CapabilityCategory 的设置问题,有的设备不具备全部功能,而请求全部或请求没有的功能就可能造成这种问题,推荐写5(tt__CapabilityCategory__Media)  这是大多数设置有的能力,而且最常用。

GetProfile时错误:

其实数据在抓包过程中也能完全抓到,多次调试后,发现结构体需要的Name以及token关键字被赋值。其他的没有,说明本点返回与服务器的支持性有很大关系。及,开发过程中需要对应自己的需求,根据实际的需要和返回错误,读取返回结构体数据。

资源:

ONVIFDEVICEMANAGER下载地址:

http://pan.baidu.com/share/link?shareid=1967805400&uk=70662920&fid=3981296515

ONVIFTESTTOOL下载地址:

http://www.cr173.com/soft/66448.html

官网开发者向导资料下载地址:

http://www.onvif.org/Resources/WhitePapers.aspx

参考文章:

onvif规范的实现:使用gSOAP创建SOAP调用实例

http://blog.csdn.net/ghostyu/article/details/8162280

Onvif开发之服务端成功对接Rtsp视频流篇

http://blog.csdn.net/max_min_go/article/details/17964643

linux设备上的Onvif 实现10:获取支持通道的RTSP地址

http://gaohtao.blog.163.com/blog/static/58241823201381113214599/

Onvif开发之客户端鉴权获取参数篇

http://blog.csdn.net/max_min_go/article/details/17617057

ONVIF协议开发资源

http://www.csdn.net/tag/onvif%252520%2525E5%25258D%25258F%2525E8%2525AE%2525AE

onvif开发之设备发现功能的实现

http://blog.csdn.net/love_xjhu/article/details/11821037

Linux设备上的Onvif实现16:实现Onvif鉴权

http://blog.csdn.net/u012084827/article/details/19031969

Onvif开发之Linux下gsoap的使用及移植

http://blog.csdn.net/love_xjhu/article/details/9772361

onvif开发总结

http://blog.csdn.net/zsl461975543/article/details/8971143

代码框架生成之Onvif开发

http://www.yc-edu.org/C__peixun/6655.html

linux设备上的Onvif 实现4:成功编译gsoap 2.8.15

http://blog.csdn.net/u012084827/article/details/12202133

onvif规范的实现:onvif开发常用调试方法 和常见的segmentation fault错误

http://blog.csdn.net/ghostyu/article/details/8432760

linux设备上的Onvif 实现6:获取摄像头的流媒体地址完整流程

http://blog.csdn.net/u012084827/article/details/12201997

S​O​A​P​ ​错​误​代​码​表

http://wenku.baidu.com/link?url=rujSmnpjBxjS3mGZrejoVVOShcPu_5Wu_9RKrQ6qWCB12xrZUvVoFkYRepLu0y6oTk6-bB5AnJ_7KxF6s8rXcb1BFko6DbBpXg0_7G0D7cu

linux设备上的Onvif 实现8:编写媒体信息获取程序

http://blog.csdn.net/u012084827/article/details/12201897

【视频开发】ONVIF客户端搜索设备获取rtsp地址开发笔记(精华篇)相关推荐

  1. ONVIF客户端搜索设备获取rtsp地址开发笔记(精华篇)

    概要:   目前ONVIF协议家族设备已占据数字监控行业半壁江山以上,亲,作为开发者的你还在犹豫是否了解下吗?本文介绍了ONVIF客户端从设备搜索,鉴权,能力获取,媒体信息获取,URI地址获取的整套流 ...

  2. ONVIFclient搜索设备获取rtsp地址开发笔记(精华篇)

    概要: 眼下ONVIF协议家族设备已占领数字监控行业半壁江山以上,亲,作为开发人员的你还在犹豫是否了解下吗?本文介绍了ONVIFclient从设备搜索.鉴权,能力获取,媒体信息获取.URI地址获取的整 ...

  3. ONVIFclient搜索设备获取rtsp解决开发笔记(精华文章)

    总结: 眼下ONVIF协议系列设备已经超过一半的数字监控行业占据更多,关闭,作为一个开发者,你还在犹豫下就明白了?本文介绍了ONVIFclient从搜索,认证,获取,媒体信息获取.URI地址获取的整套 ...

  4. Atitit onvif协议获取rtsp地址播放java语言 attilx总结

    Atitit onvif协议获取rtsp地址播放java语言 attilx总结 1.1. 获取rtsp地址的算法与流程1 1.2. Onvif摄像头的发现,ws的发现机制,使用xcf类库1 2. 调用 ...

  5. 【操作教程】RTSP/GB28181/SDK/Ehome协议人脸识别/车牌识别视频智能分析系统EasyCVR如何获取播放地址和流地址?

    视频联网共享服务EasyCVR人脸识别/车牌识别智能分析系统是一个集视频联网共享.存储.流媒体转发.视频转码.视频上云.智能分析等多种功能为一体的流媒体视频服务融合型平台.目前EasyCVR还可以集成 ...

  6. iOS7以下设备获取mac地址

    注意,是iOS7以下的设备,直接上源码,获取mac地址都是为了唯一标识一个设备的,但iOS7设备的mac地址为 020000000000 MacAddress.h #include <sys/s ...

  7. uni-app开发微信小程序之获取当前地址

    <script>import amapFile from '../../common/amap-wx.js'export default {data() {return {key: 'ea ...

  8. (017)java后台开发之客户端通过HTTP获取接口Json数据

    通过前面的文章,我们能了解javaWeb工程的基本知识.然后最激动的东西来了!我们建立了JsonTest工程,部署到了tomcat,本地浏览器访问了我们的JsonTest工程的index.jsp页面. ...

  9. Scrapy爬虫实践之搜索并获取前程无忧职位信息(基础篇)

    一.开发环境 OS:Windows 7 64bit旗舰版                Python:2.7.10                Scrapy:1.0.3               ...

最新文章

  1. 170个新项目,579个活跃代码仓库,Facebook开源年度回顾
  2. 初识元学习-Meta learning
  3. 解决在C#(.net)按字节数截取字符串最后出现乱码的问题
  4. 【Python】【函数式编程】
  5. 【阿里云产品公测】大数据下精确快速搜索OpenSearch
  6. 201107阶段二Linux-qt编程
  7. mysql utf8 4位_mysql中utf8和utf8mb4区别
  8. linux系统 远程桌面连接到服务器,Ubuntu 14.04服务器远程桌面连接
  9. mysql 环形主从_【每日一博】MySQL 互为主从(环形结构)_MySQL
  10. python求小于n的所有素数_关于求N以内素数的python实现以及优化方法
  11. 零基础学启发式算法(5)-遗传算法 (Genetic Algorithm)
  12. mmap映射方式读写本地文件
  13. NGFF(M.2) m.2中Bkey接口Mkey接口有什么不同
  14. 百度高德经纬度-离线一键批量纠偏工具
  15. 【车间调度】遗传算法求解柔性作业车间调度问题
  16. SQLmap————10、sqlmap详细命令
  17. python系统函数详解
  18. Nuendo学习之旅——音程和和弦
  19. 打印机没有反应计算机管理,电脑重启后打印机驱动无反应怎么办
  20. iOS 判断手机型号及系统版本(最新)持续更新

热门文章

  1. 多波次导弹发射中的规划问题(二)
  2. 全国计算机考试第五套,计算机等级一级MS Office考试考题:第五套字处理题
  3. 惠普笔记本恢复出厂系统
  4. python画樱花(一)
  5. 寂然安静到鸿蒙翻译,明道篇
  6. python合并word表格单元格_合并Word 表格中单元格
  7. Scala - IEEE754 浮点标准与 Float / Double 转换
  8. [原创]CI持续集成系统环境--Gitlab+Gerrit+Jenkins完整对接
  9. R 多变量数据预处理_R语言数据可视化之数据分布图(直方图、密度曲线、箱线图、等高线、2D密度图)...
  10. 数学----三角函数公式推导