onvif学习笔记3:NTP命令实现的示例

对于开始接触onvif的人,相信都会被其庞大的代码吓到。一般不建议上来就看代码,而是先去了解概念,然后去官网下载Spec来看。有一定概念后,再对照着wsdl命令描述、spec描述来阅读代码,这个时候就会比较清晰了。本文就按这个思路来讲解一下NTP的设置和获取命令。

1、SetNTP

首先看设置NTP命令SetNTP的描述,地址为:http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl#op.SetNTP

如下图:

onvif学习笔记3:NTP命令实现的示例_第1张图片

“Input”处定义了命令字段及解释。第一个字段为FromDHCP,如果为1,则表示NTP地址信息是从DHCP获取的。第二个字段为NTPManual,是手动设置NTP信息,其下又有类型Type(同时给出了枚举类型),还有IPv4或IPv6,等等。

再来看spec对SetNTP命令的描述,如下图:

onvif学习笔记3:NTP命令实现的示例_第2张图片

对比上面两张图,可以看到,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)。

2、GetNTP

onvif很多命令是成对出现的,有Set也有Get。这里介绍获取NTP命令GetNTP。按例上wsdl描述,地址为:http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl#op.GetNTP。

如图:

onvif学习笔记3:NTP命令实现的示例_第3张图片

比较详细地规定各个字段名字、含义,是否是可选的。一般地Get和Set命令字段都是一一对应的。再来看看spec的定义:

onvif学习笔记3:NTP命令实现的示例_第4张图片

同样地,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的获取命令,套路和设置命令类似。要注意的是字段空间的申请,很多字段使用指针,如果需要使用这些字段,就是开辟空间。如果不留意,可能就会出现段错误。

3、参考

参考:
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 夜

你可能感兴趣的:(onvif/live555,ONVIF协议学习笔记)