上次移植的系统,编译加载都没问题,但搜索 onvif 设备失败了,经查,根本原因是 gsoap 版本不匹配导致。本文记录分析的过程。

问题

上半年进行一款飞腾板子的应用层系统移植,由于优先级不搞,加之有大量其它事务,断断续续地搞,至上个月终于有了阶段性结果,但遗留了一个问题。当时解决了封装的 onvif 相关的视频动态库编译和加载问题后,以为没问题了,测试发现 Qt 界面没有显示视频,分析日志,原来是视频动态库搜索不到设备。

首先想到网络问题,经分析排除掉。接着抓包分析,能收到回应包。再跟踪源码,最终定位到 gsoap 版本不匹配问题。

过程

原问题重现

其实之前解决加载问题本身就有问题,当时是其它部门提供 gsoap 库,但是是最新的2.8.122版本,因为找不到2.8.90版本,而直接连接新版本库链接时会报如下错误:

/home/latelee/work/videolib-debug/onvif/CMySoap.c:72: undefined reference to `soap_new_REQUIRE_lib_v20890'

经分析,和版本号有关的代码片段如下:

// stdsoap2.h
#define GSOAP_VERSION 20890// soapStub.h
#include "stdsoap2.h"
#if GSOAP_VERSION != 20890
# error "GSOAP VERSION 20890 MISMATCH IN GENERATED CODE VERSUS LIBRARY CODE: PLEASE REINSTALL PACKAGE"
#endif// stdsoap2.h
#define soap_versioning_paste(name, ext) name##_REQUIRE_lib_v##ext
#define soap_versioning_ext(name, ext) soap_versioning_paste(name, ext)
#define soap_versioning(name) soap_versioning_ext(name, GSOAP_VERSION)#define soap_init(soap) soap_init1(soap, SOAP_IO_DEFAULT)
#define soap_init1(soap, mode) soap_init2(soap, mode, mode)
#define soap_init2(soap, imode, omode) soap_versioning(soap_init)(soap, imode, omode)#define soap_new() soap_new1(SOAP_IO_DEFAULT)
#define soap_new1(mode) soap_new2(mode, mode)
#define soap_new2(imode, omode) soap_versioning(soap_new)(imode, omode)

可以看到,在调用soap_newsoap_init时,会生成和版本号有关的函数,对于 2.9.90 版本,是soap_new_REQUIRE_lib_v20890,而2.9.122,则是soap_new_REQUIRE_lib_v208122。为解决链接问题,将代码中的版本号宏定义改为最新的版本号,如此一来,加载正常。

相机端确认

因为相机并不是生产环境使用的,因此需要首先确认相机是否能正常提供 onvif 功能。将相机与电脑直连,修改IP,再找到当年测试 onvif 的工具 odtt,安装不了,但 odm 能安装成功,打开搜索,搜索不到,手动指定 IP,可找到,观察视频也正常。获取到的地址为:

http://192.168.18.168:80/onvif/device_service

设备上抓包

相机端确认正常后,接着怀疑是网络问题,因为板子上有2个网关,测试时,2个网段都在工作。为保证网络环境纯粹性,禁用另一网关,并设置默认环境,板子系统上已有tcpdump命令,用如下命令抓包:

tcpdump -i eth0 -w result.cap

将抓包文件用 scp 传输到本地,用 wireshark 工具分析,发现搜索包和回应包都有,xml 内容也是正常的。

发送包如下:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsdd="http://schemas.xmlsoap.org/ws/2005/04/discovery" xmlns:chan="http://schemas.microsoft.com/ws/2005/02/duplex" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:c14n="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:saml1="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:wsc="http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:xmime="http://tempuri.org/xmime.xsd" xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:tt="http://www.onvif.org/ver10/schema" xmlns:wsrfbf="http://docs.oasis-open.org/wsrf/bf-2" xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" xmlns:wstop="http://docs.oasis-open.org/wsn/t-1" xmlns:tdn="http://www.onvif.org/ver10/network/wsdl" xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:trt="http://www.onvif.org/ver10/media/wsdl"><SOAP-ENV:Header><wsa:MessageID/><wsa:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action></SOAP-ENV:Header><SOAP-ENV:Body><wsdd:Probe><wsdd:Types/><wsdd:Scopes/></wsdd:Probe></SOAP-ENV:Body></SOAP-ENV:Envelope>

回应包:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelopexmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:c14n="http://www.w3.org/2001/10/xml-exc-c14n#"xmlns:ds="http://www.w3.org/2000/09/xmldsig#"xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"xmlns:wsadis="http://schemas.xmlsoap.org/ws/2004/08/addressing"xmlns:wsrp="http://schemas.xmlsoap.org/rp/"xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery"xmlns:wsa5="http://www.w3.org/2005/08/addressing"xmlns:xmime="http://www.w3.org/2005/05/xmlmime"xmlns:xop="http://www.w3.org/2004/08/xop/include"xmlns:ter="http://www.onvif.org/ver10/error"xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"xmlns:soap12="http://tempuri.org/soap12.xsd"xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"xmlns:tnsn="http://www.eventextension.com/2011/event/topics"xmlns:tt="http://www.onvif.org/ver10/schema"xmlns:wsrfbf="http://docs.oasis-open.org/wsrf/bf-2"xmlns:wstop="http://docs.oasis-open.org/wsn/t-1"xmlns:tns1="http://www.onvif.org/ver10/topics"xmlns:wsrfr="http://docs.oasis-open.org/wsrf/r-2"xmlns:dndl="http://www.onvif.org/ver10/network/wsdl/DiscoveryLookupBinding"xmlns:dnrd="http://www.onvif.org/ver10/network/wsdl/RemoteDiscoveryBinding"xmlns:dn="http://www.onvif.org/ver10/network/wsdl"xmlns:tds="http://www.onvif.org/ver10/device/wsdl"xmlns:tevcp="http://www.onvif.org/ver10/events/wsdl/CreatePullPointBinding"xmlns:teve="http://www.onvif.org/ver10/events/wsdl/EventBinding"xmlns:tevp="http://www.onvif.org/ver10/events/wsdl/PullPointBinding"xmlns:tev="http://www.onvif.org/ver10/events/wsdl"xmlns:tevps="http://www.onvif.org/ver10/events/wsdl/PullPointSubscriptionBinding"xmlns:tevpsm="http://www.onvif.org/ver10/events/wsdl/PausableSubscriptionManagerBinding"xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2"xmlns:tevsm="http://www.onvif.org/ver10/events/wsdl/SubscriptionManagerBinding"xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl"xmlns:timg10="http://www.onvif.org/ver10/imaging/wsdl"xmlns:tmd="http://www.onvif.org/ver10/deviceIO/wsdl"xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl"xmlns:tptz10="http://www.onvif.org/ver10/ptz/wsdl"xmlns:tr2="http://www.onvif.org/ver20/media/wsdl"xmlns:trc="http://www.onvif.org/ver10/recording/wsdl"xmlns:trp="http://www.onvif.org/ver10/replay/wsdl"xmlns:trt="http://www.onvif.org/ver10/media/wsdl"xmlns:tse="http://www.onvif.org/ver10/search/wsdl"><SOAP-ENV:Header><wsadis:MessageID>urn:uuid:5555f550-5535-35f6-ec55-952cbd3d45191</wsadis:MessageID><wsadis:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsadis:To><wsadis:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches</wsadis:Action><d:AppSequence InstanceId="0" MessageNumber="10"></d:AppSequence></SOAP-ENV:Header><SOAP-ENV:Body><d:ProbeMatches><d:ProbeMatch><wsadis:EndpointReference><wsadis:Address>urn:uuid:5555f550-5535-35f6-ec55-952cbd3d45191</wsadis:Address></wsadis:EndpointReference><d:Types>dn:NetworkVideoTransmitter tds:Device</d:Types><d:Scopes>onvif://www.onvif.org/type/video_encoder onvif://www.onvif.org/type/ptz onvif://www.onvif.org/type/audio_encoder onvif://www.onvif.org/location/city/hangzhou onvif://www.onvif.org/Profile/Streaming onvif://www.onvif.org/Profile/G onvif://www.onvif.org/Profile/T onvif://www.onvif.org/hardware/D2150-10-SIU onvif://www.onvif.org/name/D2150-10-SIU </d:Scopes><d:XAddrs>http://192.168.18.168:80/onvif/device_service</d:XAddrs><d:MetadataVersion>1</d:MetadataVersion></d:ProbeMatch></d:ProbeMatches></SOAP-ENV:Body>
</SOAP-ENV:Envelope>

调试源代码

外部因素排除外,只能跟踪源码了——这是我最不想做的事。由于代码原本为动态库,首先改造为可执行程序,为了兼容动态库,使用自编的 Makefile,再分析 eclipse 工程,添加涉及的库、头文件路径,编译通过后执行,在搜索设备的soap_recv___wsdd__ProbeMatches函数出错,返回3。跟踪该函数代码:

SOAP_FMAC5 int SOAP_FMAC6 soap_recv___wsdd__ProbeMatches(struct soap *soap, struct __wsdd__ProbeMatches *_param_1)
{soap_default___wsdd__ProbeMatches(soap, _param_1);soap_begin(soap);int ret = 0;ret = soap_begin_recv(soap);
if (ret) {printf("!!! %d return. %d soap err: %d\n", __LINE__, ret, soap->error); return ret;}ret = soap_envelope_begin_in(soap);
if (ret) {printf("!!! %d return. %d soap err: %d\n", __LINE__, ret, soap->error); return ret;}ret = soap_recv_header(soap);
if (ret) {printf("!!! %d return. %d soap err: %d\n", __LINE__, ret, soap->error); return ret;}ret = soap_body_begin_in(soap);if (ret) {printf("!!! %d return. %d soap err: %d\n", __LINE__, ret, soap->error); return ret;}ret = soap_closesock(soap);if (ret) {printf("!!! %d return. %d soap err: %d\n", __LINE__, ret, soap->error); return ret;}

发现在soap_body_begin_in函数出错,返回值 3,宏定义为SOAP_TAG_MISMATCH,相关定义如下:

// stdsoap2.h
#define SOAP_EOF                        EOF
#define SOAP_OK                         0
#define SOAP_CLI_FAULT                  1
#define SOAP_SVR_FAULT                  2
#define SOAP_TAG_MISMATCH               3

soap_body_begin_in函数定义如下:

soap_body_begin_in(struct soap *soap)
{if (soap->version == 0)return SOAP_OK;soap->part = SOAP_IN_BODY;if (soap_element_begin_in(soap, "SOAP-ENV:Body", 0, NULL))return soap->error;if (!soap->body)soap->part = SOAP_NO_BODY;return SOAP_OK;
}

跟踪soap_element_begin_in函数,发现只要soap结构体的other字段为1,即返回SOAP_TAG_MISMATCH

soap_element_begin_in(struct soap *soap, const char *tag, int nillable, const char *type)
{if (!soap_peek_element(soap)){if (soap->other)return soap->error = SOAP_TAG_MISMATCH;if (tag && *tag == '-')return SOAP_OK;...}return soap->error;
}

跟踪soap_peek_element函数,和other字段有关的代码如下:

soap_peek_element(struct soap *soap)
{soap->other = 0;{else if (!soap_match_tag(soap, tp->name, "SOAP-ENV:actor")){if ((!soap->actor || strcmp(soap->actor, tp->value))&& strcmp(tp->value, "http://schemas.xmlsoap.org/soap/actor/next"))soap->other = 1;}}else if (soap->version == 2){
#ifndef WITH_NOIDREFif (!soap_match_tag(soap, tp->name, "SOAP-ENC:id")){}else
#endifif (!soap_match_tag(soap, tp->name, "SOAP-ENC:itemType")){}else if (!soap_match_tag(soap, tp->name, "SOAP-ENV:role")){if ((!soap->actor || strcmp(soap->actor, tp->value))&& strcmp(tp->value, "http://www.w3.org/2003/05/soap-envelope/role/next"))soap->other = 1;}}else{}}
}

大意是,如果tag匹配了SOAP-ENV:actorSOAP-ENV:role,但某些值却不匹配,就设置other为1,最终返回不匹配错误。

分析对比了 2.8.90版本源码,该函数的判断部分没有改动,至于为什么旧版本可以,暂未知。

为了一探究竟,将相关函数移动到测试代码中,但牵扯到函数太多,最终发现soap结构体字段不相同——特别是有个在使用的函数指针是新版本才有的,于是放弃此路。

其实,到此时已经明确知道了就是 gsoap库版本问题,于是只能继续找原版本的源码来编译了。

重新编译库

在网上搜索 gsoap,很少能找到官方的版本,默认提供的是最新版本的 gsoap,但皇天不负有心人,还是找到官方的 svn 仓库https://sourceforge.net/p/gsoap2/code/177/log/?path=,相关信息如下:

[r172] by  engelen  2019-08-14 13:32:42
gSOAP 2.8.90 stable

源码只能用 svn 下载:

https://sourceforge.net/p/gsoap2/code/HEAD/tree/

下载后,切换到 172 分支,导出,打包,传输到板子系统上进行编译。

首先安装依赖包:

sudo yum install autoconf automake flex bison openssl-devel zlib-devel -y

配置编译gsoap:

./configure prefix=/home/latelee/tools/soap && make && make install

提示aclocal命令找不到:

CDPATH="${ZSH_VERSION+.}:" && cd . && /bin/sh /home/latelee/work/gsoap-2.8.90/missing aclocal-1.16
/home/latelee/work/gsoap-2.8.90/missing: line 81: aclocal-1.16: command not found
WARNING: 'aclocal-1.16' is missing on your system.

使用 yum 安装最高只有 1.13 版本:

$ /usr/bin/aclocal --version
aclocal (GNU automake) 1.13.4

根据错误提示安装1.16版本:

wget http://ftp.gnu.org/gnu/automake/automake-1.16.1.tar.gz
tar -xf automake-1.16.1.tar.gz
cd automake-1.16.1/
./configure
make
sudo make install

再次编译安装:

./configure prefix=/home/latelee/tools/soap && make && make install

得到如下文件:

libgsoap.a    libgsoapck.a    libgsoapssl.a    pkgconfig
libgsoap++.a  libgsoapck++.a  libgsoapssl++.a

视频动态库使用了 gsoap 库(具体名称为libgsoapssl.so),但默认编译没有动态库,为了减少库的依赖,在 Makefile 中指定静态库文件libgsoapssl.a,但是编译出错:

Generating dynamic lib file... libvideolib.so
/usr/bin/ld: /home/latelee/work/videolib-debug/soap/lib/libgsoapssl.a(libgsoapssl_a-stdsoap2_ssl.o): relocation R_AARCH64_ADR_PREL_PG_HI21 against external symbol `GENERAL_NAME_free@@libcrypto.so.10' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: /home/latelee/work/videolib-debug/soap/lib/libgsoapssl.a(libgsoapssl_a-stdsoap2_ssl.o)(.text+0x1ff68): unresolvable R_AARCH64_ADR_PREL_PG_HI21 relocation against symbol `GENERAL_NAME_free@@libcrypto.so.10'
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status

不知何故无法链接,只能按原来的做法,重新编译 gsoap,指定动态库:

./configure prefix=/home/latelee/tools/soap \
–-enable-shared

但失败,不支持动态库的生成:

checking build system type... Invalid configuration `–-enable-shared': machine `–-enable' not recognized
configure: error: /bin/sh ./config.sub –-enable-shared failed

想着改编译脚本生成,但太复杂了,于是根据编译过程的输出内容,从生成静态库的相关命令找到目标文件,再重新链接成动态库,手动生成命令如下:

gcc -shared -fPIC -o libgsoapssl.so libgsoapssl_a-stdsoap2_ssl.o libgsoapssl_a-dom.o

虽然有警告,但能成功生成libgsoapssl.so文件,

/usr/bin/ld: libgsoapssl_a-stdsoap2_ssl.o: relocation R_AARCH64_ADR_PREL_PG_HI21 against external symbol `namespaces' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: libgsoapssl_a-stdsoap2_ssl.o: relocation R_AARCH64_ADR_PREL_PG_HI21 against external symbol `GENERAL_NAME_free' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: libgsoapssl_a-stdsoap2_ssl.o: relocation R_AARCH64_ADR_PREL_PG_HI21 against external symbol `X509V3_conf_free' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: libgsoapssl_a-stdsoap2_ssl.o: relocation R_AARCH64_ADR_PREL_PG_HI21 against external symbol `X509V3_conf_free' can not be used when making a shared object; recompile with -fPIC

根据经验分析,代码中没有使用有与X509有关的函数,后面是否使用,届时再看。

得到动态库,再编译就正常了。测试程序也能搜索到设备了。

其它

根据以往的经验,记录一下不同相机的rtsp地址(仅个人经验):

海康摄像机:
rtsp://172.18.203.44:554/Streaming/Channels/1?transportmode=unicast&profile=Profile_1华为相机:
rtsp://192.168.18.168:554/LiveMedia/ch1/Media1

在测试时发现,必须设置默认网关才能正常搜索到设备,否则,哪怕指定了静态路由也不行。因为在实验环境中才有双网络,因此该问题没有深入研究。

小结

onvif 的框架代码,经典者如soapClient.csoapC.c等,是用 gsoap 根据 onvif 的 wsdl 文件生成的,一旦使用了某个 gsoap 版本的代码,就定型了,如果更换,则必须重新生成框架代码。

代码可以再优化一下,由于问题卡在设备搜索,可以在配置文件中添加选项,可选择跳过搜索步骤,直接连接设备,再根据 xml 分析出 rtsp 地址。但是因为时间原因,而且面对的是传承3年的代码,修改有一定困难。

后记

视频库用的 gsoap 版本是 2019 年8月14日发布的,距今近3年了。

回查工作手账,其后的一天,重构后的二期充电桩系统平稳上线;其后的一个月,因一直未发工资,内部讨论如何有序安全离职;再其后的一个月,正式提交辞呈。

而 onvif 系列文章,其前4月写了上一篇,其3年前则是前一篇。

如今又搞 onvif,天意何时何地都在。

ONVIF学习笔记11:搜索设备不匹配问题排查相关推荐

  1. onvif学习笔记9:OSD命令学习

    几个月前写了篇介绍OSD坐标系统的文章:<onvif学习笔记6:onvif的OSD坐标小记>,但没有涉及接口,因为当时并不懂.后面发现网络上除了ONVIF官网外,基本没有什么资料介绍ONV ...

  2. onvif学习笔记7:一个C++封装的onvif代码的阅读笔记

    在前面的文章<onvif学习笔记4:Windows环境使用gsoap生成onvif框架代码>.<onvif学习笔记5:onvif框架代码初步了解>中,我们了解了如何生成不同的版 ...

  3. 华为HCIA-datacom 学习笔记11——AAA原理与配置

    华为HCIA-datacom 学习笔记11--AAA原理与配置 AAA原理与配置 1.AAA概述 认证(authentication):验证用户是否获得访问权,确定哪些用户可以访问网络 授权(auth ...

  4. 从零写一个具有IOC-AOP-MVC功能的框架---学习笔记---11. MVC功能之http请求处理器的编写---简易框架最后一公里!

    从零写一个具有IOC-AOP-MVC功能的框架-学习笔记 专栏往期文章链接: IOC功能相关章节: 从零写一个具有IOC-AOP-MVC功能的框架-学习笔记-01.项目初始化 从零写一个具有IOC-A ...

  5. SpringMVC:学习笔记(11)——依赖注入与@Autowired

    SpringMVC:学习笔记(11)--依赖注入与@Autowired 使用@Autowired 从Spring2.5开始,它引入了一种全新的依赖注入方式,即通过@Autowired注解.这个注解允许 ...

  6. Hadoop学习笔记—11.MapReduce中的排序和分组

    Hadoop学习笔记-11.MapReduce中的排序和分组 一.写在之前的 1.1 回顾Map阶段四大步骤 首先,我们回顾一下在MapReduce中,排序和分组在哪里被执行: 从上图中可以清楚地看出 ...

  7. HALCON 20.11:深度学习笔记(11)---目标检测

    HALCON 20.11:深度学习笔记(11)---目标检测 HALCON 20.11.0.0中,实现了深度学习方法. 本章讲解了如何使用基于深度学习的对象检测. 通过对象检测,我们希望在图像中找到不 ...

  8. 台大李宏毅Machine Learning 2017Fall学习笔记 (11)Convolutional Neural Network

    台大李宏毅Machine Learning 2017Fall学习笔记 (11)Convolutional Neural Network 本博客主要整理自: http://blog.csdn.net/x ...

  9. 点云学习笔记11——VoxelNet算法+代码运行

    点云学习笔记11--VoxelNet算法+代码运行 一.算法分析 摘要 介绍 相关工作 1.2. 贡献 2.VoxelNet 2.1.特征学习网络 2.1.1 特征学习网络 二.代码复现 2.1.环境 ...

最新文章

  1. 基于SSM实现社区医院管理系统
  2. 【转】android自定义控件
  3. USACO1.5 Number Triangles(numtri)
  4. java服务端项目开发规范
  5. 现代制造工程课堂笔记07——应力应变分析(考点应力莫尔圆)
  6. qmediaplayer获取流类型_Java 流API
  7. 起点低,是彪悍的最好证明!
  8. Spark学习之RDD的概念
  9. 重磅推荐!日立开源语义分割数据集标注工具Semantic Segmentation Editor
  10. 诗和远方:无题(五十)
  11. linux文件系统格式化
  12. 2016峰会:项目管理与高级项目管理(广州站)
  13. 计算机主机拆解报告心得体会,计算机拆装实验报告心得体会(共10篇).doc
  14. OpenSER安装配置指南
  15. 数据清洗Chap4——dataframe操作
  16. 最新云优YUNUCMS企业网站管理系统
  17. 富士通服务器远程控制,iRMC远程管理功能
  18. yml文件读取方式_1
  19. linux pack文件镜像,使用buildpack-deps 构建自己的入容器镜像
  20. 不规则四面体知道六边的体积公式

热门文章

  1. Spring boot整合Redis(入门教程)
  2. The system cannot find the path specified
  3. Vue完成的图片转换
  4. 北航软件测评中心 招聘FPGA测试工程师
  5. 模式识别 | PRML概览
  6. linux的版本(部分转载)
  7. BSN-DDC基础网络的DID功能设计
  8. 2017年Q1中国无线路由器市场研究报告
  9. mysql 有newid()_MySQL中,有无GUID函数?就像SQL Server中的newid()解决方法
  10. 特征图谱字典_空间数据图谱为特征