接触soap和xml第三天,稍有一点认识,看了很多文章,都不适合我的项目。目前项目中用到三台不同品牌的IPC,虽说厂家都用SDK,但在项目中引入三套SDK有点臃肿,何况目前已实现了使用VLC的拉流播放,就差云台控制了。只有一条路了就是使用ONVIF,C#中简单,C++都是使用gsoap,走了一遍流程,太麻烦,太复杂,使用WSDL文件生的代码好几兆不说,需要引用的源文件一大堆!现在就是要简单的实现云台控制PTZ,没必要搞这么复杂,研究了一天,不按寻常路也是挺香!
需要的工具:WireShark、XMLspy、ONVIF Device Test Tool。
具体思路:控制PTZ使用的是soap协议,soap协议可以通过微软的Soap ToolKit3.0来实现,现在就差具体的控制协议了,也就是XML消息,获取XML消息的流程:
1、使用ONVIF Device Test Tool连接IPC
首先用ONVIF Device Test Tool连接IPC,这一块文章很多,就不多说了,然后进行云台控制,测试没有问题后进行下一步。
2、使用WireShark抓包查看XML
第一步实现后就可以使用WireShark抓包,启动本地连接的捕获,然后用ONVIF Device Test Tool控制PTZ,根据IPC的IP地址查看HTTP包,得到控制连续移动的XML如下:
拷贝出XML后的消息如下:
admin
6UOJDvX8SFsITkYmBA/wltXMC2A=
i+bhTDOL/vrj6s6Tox5TxQ==
2022-11-13T06:36:19Z
Profile_1
3、使用XMLspy测试xml消息(本步骤为可选项)
使用XMLspy,在 SOAP Debugger中SOAP菜单中建立一个新的Request,可以随便用一个WSDL文件建立,然后使用SOAP菜单下的Change SOAP Request Parameters修改Connection Endpoint为IPC的PTZ控制地址就好,这个地址在第一步PTZ URL中可以看到。然后使用Send Request to Server测试,IPC正常工作,这个xml消息就OK了。
其实,如果对1、2步骤操作没问题的话,XML消息也不需要测试,也就是说即使不适用XMLspy也不影响结果。
同样的方法可获得控制PTZ的三个参数的控制XML消息。
如果感觉上述步骤麻烦,也可以使用gsoap来生产XML消息文件,实际上就是仅仅使用gsoap的wsdl2h和soapccp2这两个可执行文件,具体操作文章很多,我参考的是流浪的Coder博客园的文章使用gSOAP工具生成onvif框架代码,这篇写的比较细,有一个问题就是我使用下载的PTZ.wsdl文件生成.h文件出错,使用在线的可以正常生成,生成C代码后,在目录中会有很多xml文件,找到相应的文件就可以了。
建立一个控制台VC++程序,引入Soap ToolKit3.0,具体操作见我上一篇文章:SOAP学习之一:Visual C++创建简单的客户端--使用soap toolkit 3.0获取UTC服务器时间。
这里面主要有两个问题,费了不少周折:
1、使用WriteXml方法
大多数介绍Soap ToolKit的文章都是使用的StartElement方法创建消息元素,但这做只能限定到固定的格式,根据PTZ的控制协议,无法使用该方法创建消息,找了好久,最后还是看了soap toolkit 3.0自带的帮助文件,看到了WirteXml方法,这下好了,可以为所欲为了:
2、鉴权问题
常规的鉴权是通过HTTP连接时鉴权,看到soap toolkit 3.0的HttpConnector30有两个属性:AuthUser和AuthPassword,想着这下没有问题了,但是测试中出现未鉴权的错误,搜来搜去,还是看了帮助文件,发现鉴权方案属性:
默认的不是摘要认证( Digest authentication),添加该属性赋值0x08搞定!
还有一种方法是通过XML消息里的 Header鉴权,这里面涉及密文传输,懒得去研究,直接抓包后把相应消息放入Header中也能实现,这种方法较笨!
解决了以上连个问题,剩下的就简单了,全部代码如下,实现了海康IPC的PTZ控制(PTZ通过IPC的485输出到云台),其中,两种鉴权方式都有使用,实现了连续移动和停止,在控制台窗口输入m移动,输入s停止,输入a是使用Header鉴权的停止,代码尽量使用了标准 XML元素的创建,只有实在不行才使用WriteXml方法:
#include "stdafx.h"
#import "msxml4.dll"
//using namespace MSXML2;
#import "C:\Program Files (x86)\Common Files\MSSoap\Binaries\mssoap30.dll" \
exclude("IStream", "IErrorInfo", "ISequentialStream", "_LARGE_INTEGER", \
"_ULARGE_INTEGER", "tagSTATSTG", "_FILETIME")
using namespace MSSOAPLib30;
void Move(void)
{
ISoapSerializerPtr Serializer;
ISoapConnectorPtr Connector;
// Connect to the service.
if(FAILED(Connector.CreateInstance(__uuidof(HttpConnector30)))) //创建对象
{
wprintf(_T("failed"));
}
// Connect to the service
Connector->Property["EndPointURL"] ="http://192.168.0.64:/onvif/PTZ";
Connector->Connect();
// Begin message
Connector->Property["SoapAction"] = "";
Connector->Property["AuthUser"] = "admin";
Connector->Property["AuthPassword"] = "abc12345";
Connector->Property["WinHTTPAuthScheme"] = 0x08;
Connector->BeginMessage();
// Create the SoapSerializer
Serializer.CreateInstance(__uuidof(SoapSerializer30));
// Connect the serializer to the input stream of the connector
Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));
// Build the SOAP Message
Serializer->StartEnvelope("","","");
Serializer->StartBody("");
Serializer->StartElement("ContinuousMove","","","tptz");
Serializer->StartElement("ProfileToken","","","tptz");
Serializer->WriteString("Profile_1");
Serializer->EndElement();
Serializer->StartElement("Velocity","","","tptz");
Serializer->WriteXml(""); //X轴移动,参数为移动速度,其他轴参考这个修改
Serializer->WriteXml(" ");
Serializer->WriteXml("");
Serializer->WriteXml(" ");
Serializer->EndElement();
Serializer->WriteXml("0 ");
Serializer->EndElement();
Serializer->EndBody();
Serializer->EndEnvelope();
// Send the message to the web service
Connector->EndMessage();
}
void StopA(void)
{
ISoapSerializerPtr Serializer;
ISoapConnectorPtr Connector;
if(FAILED(Connector.CreateInstance(__uuidof(HttpConnector30)))) //创建对象
{
wprintf(_T("failed"));
}
// Connect to the service
Connector->Property["EndPointURL"] ="http://192.168.0.64:/onvif/PTZ";
Connector->Connect();
// Begin message
Connector->Property["SoapAction"] = "";
Connector->BeginMessage();
// Create the SoapSerializer
Serializer.CreateInstance(__uuidof(SoapSerializer30));
// Connect the serializer to the input stream of the connector
Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));
// Build the SOAP Message
Serializer->StartEnvelope("","","");
Serializer->StartHeader("");
Serializer->WriteXml("");
Serializer->WriteXml("admin ");
Serializer->WriteXml("jBnekXflgIuMFm4EPFJpKZ38Mvk= ");
Serializer->WriteXml("L5gyUZm2OWJmkX+MRNWDOA== 2022-11-13T02:54:41Z ");
Serializer->WriteXml(" ");
Serializer->EndHeader();
Serializer->StartBody("");
Serializer->StartElement("Stop","","","tptz");
Serializer->StartElement("ProfileToken","","","tptz");
Serializer->WriteString("Profile_1");
Serializer->EndElement();
Serializer->StartElement("Velocity","","","tptz");
Serializer->WriteString("true");
Serializer->EndElement();
Serializer->StartElement("Timeout","","","tptz");
Serializer->WriteString("0");
Serializer->EndElement();
Serializer->EndElement();
Serializer->EndBody();
Serializer->EndEnvelope();
// Send the message to the web service
Connector->EndMessage();
}
void Stop(void)
{
ISoapSerializerPtr Serializer;
ISoapConnectorPtr Connector;
if(FAILED(Connector.CreateInstance(__uuidof(HttpConnector30)))) //创建对象
{
wprintf(_T("failed"));
}
// Connect to the service
Connector->Property["EndPointURL"] ="http://192.168.0.64:/onvif/PTZ";
Connector->Connect();
// Begin message
Connector->Property["SoapAction"] = "";
Connector->Property["AuthUser"] = "admin";
Connector->Property["AuthPassword"] = "abc12345";
Connector->Property["WinHTTPAuthScheme"] = 0x08;
Connector->BeginMessage();
// Create the SoapSerializer
Serializer.CreateInstance(__uuidof(SoapSerializer30));
// Connect the serializer to the input stream of the connector
Serializer->Init(_variant_t((IUnknown*)Connector->InputStream));
// Build the SOAP Message
Serializer->StartEnvelope("","","");
Serializer->StartBody("");
Serializer->StartElement("Stop","","","tptz");
Serializer->StartElement("ProfileToken","","","tptz");
Serializer->WriteString("Profile_1");
Serializer->EndElement();
Serializer->StartElement("Velocity","","","tptz");
Serializer->WriteString("true");
Serializer->EndElement();
Serializer->StartElement("Timeout","","","tptz");
Serializer->WriteString("0");
Serializer->EndElement();
Serializer->EndElement();
Serializer->EndBody();
Serializer->EndEnvelope();
// Send the message to the web service
Connector->EndMessage();
}
int _tmain(int argc, _TCHAR* argv[])
{
char str;
CoInitialize(NULL);
while(1)
{
str=getchar();
if(str=='m')
{
Move();
printf("X Axis Continous Move\n");
}
else if(str=='s')
{
Stop();
printf("Stop by Header Authentication\n");
}
else if(str=='a')
{
StopA();
printf("Stop by HttpConnect Authentication\n");
}
else if(str=='q')
break;
}
CoUninitialize();
return 0;
}
代码目前没有返回数据,以后完善,不影响控制,发送命令后可以看到IPC返回OK消息:
程序实现了单独的PTZ控制,代码量非常少,个人认为是目前我能看到的唯一使用 soap toolkit实现的PTZ控制代码。
另外搜索资料时个人心得首先 使用非某度搜索首推某歌,不行的话使用必应,这样少看广告,效率高,特别是搜外文资料。其次搜索顺序先国内不行外文,搜索结果两页之内找不到有价值的,要么关键词有问题,要么不要再搜了,自己研究吧!!大多数文章就是抄来抄去,看的实在太累!!