对于开始接触onvif的人,相信都会被其庞大的代码吓到。一般不建议上来就看代码,而是先去了解概念,然后去官网下载Spec来看。有一定概念后,再对照着wsdl命令描述、spec描述来阅读代码,这个时候就会比较清晰了。本文就按这个思路来讲解一下NTP的设置和获取命令。
首先看设置NTP命令SetNTP的描述,地址为:http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl#op.SetNTP
如下图:
“Input”处定义了命令字段及解释。第一个字段为FromDHCP,如果为1,则表示NTP地址信息是从DHCP获取的。第二个字段为NTPManual,是手动设置NTP信息,其下又有类型Type(同时给出了枚举类型),还有IPv4或IPv6,等等。
再来看spec对SetNTP命令的描述,如下图:
对比上面两张图,可以看到,spec的字段不是太详细,但它给出了错误码Fault codes。有的错误码在代码里是需要设置的(下面的示例伪代码就使用到)。
伪代码示例如下:
SOAP_FMAC5 int SOAP_FMAC6 __tds__SetNTP(struct soap* soap, struct _tds__SetNTP *tds__SetNTP, struct _tds__SetNTPResponse *tds__SetNTPResponse)
{
onvif_debug(7, "--%s--\n", __func__);
// 不支持FromDHCP,所以返回错误
if (tds__SetNTP->FromDHCP == TRUE)
{
onvif_fault(soap,"ter:NotSupported", "ter:SetDHCPNotAllowed");
return SOAP_FAULT;
}
// 不支持IPv6Address,所以返回错误
if (tds__SetNTP->NTPManual->IPv6Address != NULL)
{
onvif_fault(soap,"ter:NotSupported", "ter:IPv6AddressNotAllowed");
return SOAP_FAULT;
}
// 只支持IPv4Address
if(tds__SetNTP->NTPManual->IPv4Address != NULL)
{
// 地址非法,返回错误
if(isValidIp4(tds__SetNTP->NTPManual->IPv4Address[0]) == 0)
{
onvif_fault(soap,"ter:InvalidArgVal", "ter:InvalidIPv4Address");
return SOAP_FAULT;
}
sa.sin_family = AF_INET;
inet_aton(tds__SetNTP->NTPManual->IPv4Address[0], &sa.sin_addr.s_addr);
s = getnameinfo(&sa, sizeof(sa), host, sizeof(host), service, sizeof(service), NI_NAMEREQD); //转换host
// 如果host非法,返回错误
if(isValidHostname(host) == 0)
{
onvif_fault(soap,"ter:InvalidArgVal", "ter:InvalidDnsName");
return SOAP_FAULT;
}
// 这里设置设备的NTP服务器地址...
}
return SOAP_OK;
}
涉及到的结构体定义如下:
struct _tds__SetNTP
{
/// @brief Indicate if NTP address information is to be retrieved using DHCP.
/// Element FromDHCP of type xs:boolean.
enum xsd__boolean FromDHCP 1; ///< Required element.
/// @brief Manual NTP settings.
/// Size of array of struct tt__NetworkHost* is 0..unbounded
$int __sizeNTPManual 0;
/// Array struct tt__NetworkHost* of length 0..unbounded
struct tt__NetworkHost* NTPManual 0;
};
struct tt__NetworkHost
{
/// @brief Network host type: IPv4, IPv6 or DNS.
/// Element Type of type "http://www.onvif.org/ver10/schema":NetworkHostType.
enum tt__NetworkHostType Type 1; ///< Required element.
/// @brief IPv4 address.
/// Element IPv4Address of type "http://www.onvif.org/ver10/schema":IPv4Address.
tt__IPv4Address IPv4Address 0; ///< Optional element.
/// @brief IPv6 address.
/// Element IPv6Address of type "http://www.onvif.org/ver10/schema":IPv6Address.
tt__IPv6Address IPv6Address 0; ///< Optional element.
/// @brief DNS name.
/// Element DNSname of type "http://www.onvif.org/ver10/schema":DNSName.
tt__DNSName DNSname 0; ///< Optional element.
/// Element Extension of type "http://www.onvif.org/ver10/schema":NetworkHostExtension.
struct tt__NetworkHostExtension* Extension 0; ///< Optional element.
///
/// TODO: Schema extensibility is user-definable.
/// Consult the protocol documentation to change or insert declarations.
/// Use wsdl2h option -x to remove this attribute.
/// Use wsdl2h option -d for xsd__anyAttribute DOM (soap_dom_attribute).
@_XML __anyAttribute ; ///< A placeholder that has no effect: please see comment.
};
小结:onvif的设置命令,基本套路的根据文档(建议同时看wsdl及spec)知道有哪些字段,然后结合实际情况填充(比如SetNTP,不支持的都返回错误码),在此过程可能要实现部分功能函数(如获取IP)。
如图:
比较详细地规定各个字段名字、含义,是否是可选的。一般地Get和Set命令字段都是一一对应的。再来看看spec的定义:
同样地,spec也是大概地描述字段,没有很详细。
接下来看看SetNTP命令的代码实现,如下:
SOAP_FMAC5 int SOAP_FMAC6 __tds__GetNTP(struct soap* soap, struct _tds__GetNTP *tds__GetNTP, struct _tds__GetNTPResponse *tds__GetNTPResponse)
{
char *ip_address = getIPAddress(); // 获取IP地址;
int dhcp_enable = getDhcpStatus(); // 获取dhcp使能标志
tds__GetNTPResponse->NTPInformation = (struct tt__NTPInformation*)soap_malloc(soap, sizeof(struct tt__NTPInformation));
tds__GetNTPResponse->NTPInformation->FromDHCP = dhcp_enable;
if(dhcp_enable == 1) // NTPFromDHCP
{
tds__GetNTPResponse->NTPInformation->__sizeNTPFromDHCP = 1;
// 开辟空间
tds__GetNTPResponse->NTPInformation->NTPFromDHCP = (struct tt__NetworkHost*)soap_malloc(soap, sizeof(struct tt__NetworkHost));
tds__GetNTPResponse->NTPInformation->NTPFromDHCP->Type = IPv4; // 枚举类型:enum { 'IPv4', 'IPv6', 'DNS' }
tds__GetNTPResponse->NTPInformation->NTPFromDHCP->IPv4Address = (char **)soap_malloc(soap, sizeof(char *));
tds__GetNTPResponse->NTPInformation->NTPFromDHCP->IPv4Address[0] = (char *)soap_malloc(soap, sizeof(char) * 128);
strcpy(tds__GetNTPResponse->NTPInformation->NTPFromDHCP->IPv4Address[0], ip_address);
tds__GetNTPResponse->NTPInformation->NTPFromDHCP->IPv6Address = NULL;
tds__GetNTPResponse->NTPInformation->NTPFromDHCP->DNSname = NULL;
tds__GetNTPResponse->NTPInformation->NTPFromDHCP->Extension = NULL;
tds__GetNTPResponse->NTPInformation->__sizeNTPManual = 0;
tds__GetNTPResponse->NTPInformation->NTPManual = NULL;
}
else // 手动
{
tds__GetNTPResponse->NTPInformation->__sizeNTPManual = 1; // 置为1
tds__GetNTPResponse->NTPInformation->NTPManual = ((struct tt__IPAddress *)soap_malloc(soap, sizeof(struct tt__IPAddress)));
tds__GetNTPResponse->NTPInformation->NTPManual->Type = IPv4;
tds__GetNTPResponse->NTPInformation->NTPManual->IPv4Address = (char **)soap_malloc(soap, sizeof(char *));
// 开辟存储IP地址空间
tds__GetNTPResponse->NTPInformation->NTPManual->IPv4Address[0] = (char *)soap_malloc(soap, sizeof(char) * 128);
strcpy(tds__GetNTPResponse->NTPInformation->NTPManual->IPv4Address[0], ip_address); // 赋值
// 下面这些没有,置为NULL
tds__GetNTPResponse->NTPInformation->NTPManual->IPv6Address = NULL;
tds__GetNTPResponse->NTPInformation->NTPManual->DNSname = NULL;
tds__GetNTPResponse->NTPInformation->NTPManual->Extension = NULL;
tds__GetNTPResponse->NTPInformation->__sizeNTPFromDHCP = 0;
tds__GetNTPResponse->NTPInformation->NTPFromDHCP = NULL;
}
return SOAP_OK;
}
小结:onvif的获取命令,套路和设置命令类似。要注意的是字段空间的申请,很多字段使用指针,如果需要使用这些字段,就是开辟空间。如果不留意,可能就会出现段错误。
参考:
onvif 设备管理:http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl
onvif最新(2015年)的核心规范:http://www.onvif.org/specs/DocMap-2.6.html
onvif规范(含profile、web serivce描述、设备测试规范):http://www.onvif.org/Documents/Specifications.aspx
李迟 2015.12.14 夜