3 Star 11 Fork 7

FlyCate / onvif

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
Device.go 14.07 KB
一键复制 编辑 原始数据 按行查看 历史
package onvif
import (
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
"gitee.com/dzhw0314/onvif/device"
"gitee.com/dzhw0314/onvif/gosoap"
"gitee.com/dzhw0314/onvif/networking"
wsdiscovery "gitee.com/dzhw0314/onvif/ws-discovery"
"github.com/beevik/etree"
)
//Xlmns XML Scheam
var Xlmns = map[string]string{
"onvif": "http://www.onvif.org/ver10/schema",
"tds": "http://www.onvif.org/ver10/device/wsdl",
"trt": "http://www.onvif.org/ver10/media/wsdl",
"tev": "http://www.onvif.org/ver10/events/wsdl",
"tptz": "http://www.onvif.org/ver20/ptz/wsdl",
"timg": "http://www.onvif.org/ver20/imaging/wsdl",
"tan": "http://www.onvif.org/ver20/analytics/wsdl",
"xmime": "http://www.w3.org/2005/05/xmlmime",
"wsnt": "http://docs.oasis-open.org/wsn/b-2",
"xop": "http://www.w3.org/2004/08/xop/include",
"wsa": "http://www.w3.org/2005/08/addressing",
"wstop": "http://docs.oasis-open.org/wsn/t-1",
"wsntw": "http://docs.oasis-open.org/wsn/bw-2",
"wsrf-rw": "http://docs.oasis-open.org/wsrf/rw-2",
"wsaw": "http://www.w3.org/2006/05/addressing/wsdl",
"tse": "http://www.onvif.org/ver10/search/wsdl",
"trp1": "http://www.onvif.org/ver10/replay/wsdl",
"trc": "http://www.onvif.org/ver10/recording/wsdl",
}
var NamespaceForPkg = map[string] string {
"http://www.onvif.org/ver10/device/wsdl": "device",
"http://www.onvif.org/ver10/media/wsdl": "media",
"http://www.onvif.org/ver10/events/wsdl": "event",
"http://www.onvif.org/ver20/ptz/wsdl": "ptz",
"http://www.onvif.org/ver20/imaging/wsdl": "imaging",
"http://www.onvif.org/ver20/analytics/wsdl": "analytics",
"http://www.onvif.org/ver10/search/wsdl": "search",
"http://www.onvif.org/ver10/replay/wsdl": "replay",
"http://www.onvif.org/ver10/recording/wsdl": "recording",
}
//DeviceType alias for int
type DeviceType int
// Onvif Device Tyoe
const (
NVD DeviceType = iota
NVS
NVA
NVT
)
func (devType DeviceType) String() string {
stringRepresentation := []string{
"NetworkVideoDisplay",
"NetworkVideoStorage",
"NetworkVideoAnalytics",
"NetworkVideoTransmitter",
}
i := uint8(devType)
switch {
case i <= uint8(NVT):
return stringRepresentation[i]
default:
return strconv.Itoa(int(i))
}
}
//DeviceInfo struct contains general information about ONVIF device
type DeviceInfo struct {
Manufacturer string
Model string
FirmwareVersion string
SerialNumber string
HardwareId string
}
//Device for a new device of onvif and DeviceInfo
//struct represents an abstract ONVIF device.
//It contains methods, which helps to communicate with ONVIF device
type Device struct {
params DeviceParams
endpoints map[string]string
info DeviceInfo
}
type DeviceParams struct {
Xaddr string
Username string
Password string
EncryptionType string
HttpClient *http.Client
UseWSUsernameToken int
HttpType int
}
//GetServices return available endpoints
func (dev *Device) GetServices() map[string]string {
return dev.endpoints
}
//GetServices return available endpoints
func (dev *Device) GetDeviceInfo() DeviceInfo {
return dev.info
}
//GetServices return DeviceParams
func (dev *Device) GetDeviceParams() DeviceParams {
return dev.params
}
func (dev *Device) SetDeviceParams(param DeviceParams) {
dev.params = param
}
//GetServices return DeviceParams
func (dev *Device) SetUseWSUsernameToken(param int) {
dev.params.UseWSUsernameToken = param
}
func readResponse(resp *http.Response) string {
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
return string(b)
}
//GetAvailableDevicesAtSpecificEthernetInterface ...
func GetAvailableDevicesAtSpecificEthernetInterfaceEx(interfaceName string) ([]Device) {
/*
Call an ws-discovery Probe Message to Discover NVT type Devices
*/
devices := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:" + NVT.String()}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"})
nvtDevices := make([]Device, 0)
for _, j := range devices {
doc := etree.NewDocument()
if err := doc.ReadFromString(j); err != nil {
fmt.Errorf("%s", err.Error())
return nil
}
endpoints := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/XAddrs")
for _, xaddr := range endpoints {
xaddr := strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2]
fmt.Println(xaddr)
c := 0
for c = 0; c < len(nvtDevices); c++ {
if nvtDevices[c].params.Xaddr == xaddr {
fmt.Println(nvtDevices[c].params.Xaddr, "==", xaddr)
break
}
}
if c < len(nvtDevices) {
continue
}
dev, err := NewDevice(DeviceParams{Xaddr: strings.Split(xaddr, " ")[0]})
if err != nil {
fmt.Println("Error", xaddr)
fmt.Println(err)
continue
} else {
nvtDevices = append(nvtDevices, *dev)
}
}
}
return nvtDevices
}
//GetAvailableDevicesAtSpecificEthernetInterface ...
func GetAvailableDevicesAtSpecificEthernetInterface(interfaceName string) []Device {
/*
Call an ws-discovery Probe Message to Discover NVT type Devices
*/
devices := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:" + NVT.String()}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"})
nvtDevices := make([]Device, 0)
for _, j := range devices {
doc := etree.NewDocument()
if err := doc.ReadFromString(j); err != nil {
fmt.Errorf("%s", err.Error())
return nil
}
endpoints := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/XAddrs")
for _, xaddr := range endpoints {
xaddr := strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2]
fmt.Println(xaddr)
c := 0
for c = 0; c < len(nvtDevices); c++ {
if nvtDevices[c].params.Xaddr == xaddr {
fmt.Println(nvtDevices[c].params.Xaddr, "==", xaddr)
break
}
}
if c < len(nvtDevices) {
continue
}
dev, err := NewDevice(DeviceParams{Xaddr: strings.Split(xaddr, " ")[0]})
if err != nil {
fmt.Println("Error", xaddr)
fmt.Println(err)
continue
} else {
nvtDevices = append(nvtDevices, *dev)
}
}
}
return nvtDevices
}
func (dev *Device) getSupportedServices(resp *http.Response) {
doc := etree.NewDocument()
data, _ := ioutil.ReadAll(resp.Body)
if err := doc.ReadFromBytes(data); err != nil {
//log.Println(err.Error())
return
}
services := doc.FindElements("./Envelope/Body/GetCapabilitiesResponse/Capabilities/*/XAddr")
for _, j := range services {
dev.addEndpoint(j.Parent().Tag, j.Text())
}
extension_services := doc.FindElements("./Envelope/Body/GetCapabilitiesResponse/Capabilities/Extension/*/XAddr")
for _, j := range extension_services {
dev.addEndpoint(j.Parent().Tag, j.Text())
}
}
//NewDevice function construct a ONVIF Device entity
func NewDevice(params DeviceParams) (*Device, error) {
dev := new(Device)
dev.params = params
dev.endpoints = make(map[string]string)
dev.addEndpoint("Device", "http://"+dev.params.Xaddr+"/onvif/device_service")
if dev.params.HttpClient == nil {
dev.params.HttpClient = new(http.Client)
}
getCapabilities := device.GetCapabilities{Category: "All"}
resp, err := dev.CallMethod(getCapabilities)
if err != nil || resp.StatusCode != http.StatusOK {
return nil, errors.New("camera is not available at " + dev.params.Xaddr + " or it does not support ONVIF services")
}
dev.getSupportedServices(resp)
return dev, nil
}
func (dev *Device) getSupportedServices2(resp networking.RestResponse) {
realData := gosoap.SoapMessage(string(resp.RespBody)).Body()
getServiceResp := device.GetServicesResponse{}
err := xml.Unmarshal([]byte(realData), &getServiceResp)
if err != nil {
fmt.Printf("解析GetService失败!err=%v", err)
return
}
for _, service := range getServiceResp.Service {
dev.addEndpoint(string(service.XAddr[strings.LastIndex(string(service.XAddr), "/")+1:]), string(service.XAddr))
}
}
func (dev *Device) getSupportedServices3(resp networking.RestResponse) {
realData := gosoap.SoapMessage(string(resp.RespBody)).Body()
getServiceResp := device.GetServicesResponse{}
err := xml.Unmarshal([]byte(realData), &getServiceResp)
if err != nil {
fmt.Printf("解析GetService失败!err=%v", err)
return
}
for _, service := range getServiceResp.Service {
fmt.Printf("设备能力 【Namespace|%v】【addr|%v】\n", string(service.Namespace), string(service.XAddr))
if _,ok := NamespaceForPkg[string(service.Namespace)]; ok == true {
dev.addEndpoint(NamespaceForPkg[string(service.Namespace)], string(service.XAddr))
}
}
}
func NewDevice2(params DeviceParams) (*Device, error) {
dev := new(Device)
dev.params = params
dev.endpoints = make(map[string]string)
dev.addEndpoint("Device", "http://"+dev.params.Xaddr+"/onvif/device_service")
dev.SetUseWSUsernameToken(0)
getCapabilities := device.GetServices{}
fmt.Println("查询设备能力集【使用Digest认证】!")
resp, req := dev.CallMethodEx(getCapabilities)
if resp.Err != nil || resp.StatusCode != http.StatusOK {
err := fmt.Errorf("查询设备【%v】能力失败!statusCode=%v err=%v respBody=%v , " +
"请检查设备信息是否配置正确 或 设备不支持ONVIF服务。 req=%v", dev.params.Xaddr, resp.StatusCode, resp.Err, string(resp.RespBody), req)
fmt.Printf("【使用Digest认证】%v\n", err)
fmt.Println("查询设备能力集【使用WSSE认证】!")
dev.SetUseWSUsernameToken(1)
resp, req = dev.CallMethodEx(getCapabilities)
if resp.Err != nil || resp.StatusCode != http.StatusOK {
err = fmt.Errorf("查询设备【%v】能力失败!statusCode=%v err=%v respBody=%v , " +
"请检查设备信息是否配置正确 或 设备不支持ONVIF服务。 req=%v", dev.params.Xaddr, resp.StatusCode, resp.Err, string(resp.RespBody), req)
fmt.Printf("【使用WSSE认证】%v\n", err)
return nil, err
}
fmt.Println("查询设备能力集【使用WSSE认证】成功!")
} else {
fmt.Println("查询设备能力集【使用Digest认证】成功!")
}
fmt.Println("能力集查询结果:", string(resp.RespBody))
dev.getSupportedServices3(resp)
return dev, nil
}
func (dev *Device) addEndpoint(Key, Value string) {
//use lowCaseKey
//make key having ability to handle Mixed Case for Different vendor devcie (e.g. Events EVENTS, events)
lowCaseKey := strings.ToLower(Key)
// Replace host with host from device params.
if u, err := url.Parse(Value); err == nil {
u.Host = dev.params.Xaddr
Value = u.String()
}
dev.endpoints[lowCaseKey] = Value
}
//GetEndpoint returns specific ONVIF service endpoint address
func (dev *Device) GetEndpoint(name string) string {
return dev.endpoints[name]
}
func (dev Device) buildMethodSOAP(msg string) (gosoap.SoapMessage, error) {
doc := etree.NewDocument()
if err := doc.ReadFromString(msg); err != nil {
//log.Println("Got error")
return "", err
}
element := doc.Root()
soap := gosoap.NewEmptySOAP()
soap.AddBodyContent(element)
return soap, nil
}
//getEndpoint functions get the target service endpoint in a better way
func (dev Device) getEndpoint(endpoint string) (string, error) {
// common condition, endpointMark in map we use this.
if endpointURL, bFound := dev.endpoints[endpoint]; bFound {
return endpointURL, nil
}
//but ,if we have endpoint like event、analytic
//and sametime the Targetkey like : events、analytics
//we use fuzzy way to find the best match url
var endpointURL string
for targetKey := range dev.endpoints {
if strings.Contains(targetKey, endpoint) {
endpointURL = dev.endpoints[targetKey]
return endpointURL, nil
}
}
//return endpointURL, errors.New("target endpoint service not found")
return endpointURL, fmt.Errorf("设备不支持此功能!<%v>", endpoint)
}
//CallMethod functions call an method, defined <method> struct.
//You should use Authenticate method to call authorized requests.
func (dev Device) CallMethod(method interface{}) (*http.Response, error) {
pkgPath := strings.Split(reflect.TypeOf(method).PkgPath(), "/")
pkg := strings.ToLower(pkgPath[len(pkgPath)-1])
endpoint, err := dev.getEndpoint(pkg)
if err != nil {
return nil, err
}
return dev.callMethodDo(endpoint, method)
}
//CallMethod functions call an method, defined <method> struct.
//You should use Authenticate method to call authorized requests.
func (dev Device) CallMethodEx(method interface{}) (networking.RestResponse, string) {
pkgPath := strings.Split(reflect.TypeOf(method).PkgPath(), "/")
pkg := strings.ToLower(pkgPath[len(pkgPath)-1])
endpoint, err := dev.getEndpoint(pkg)
if err != nil {
return networking.NewRestResponse(nil, -1, err), ""
}
return dev.callMethodDoEx(endpoint, method)
}
//CallMethod functions call an method, defined <method> struct with authentication data
func (dev Device) callMethodDo(endpoint string, method interface{}) (*http.Response, error) {
output, err := xml.MarshalIndent(method, " ", " ")
if err != nil {
return nil, err
}
soap, err := dev.buildMethodSOAP(string(output))
if err != nil {
return nil, err
}
soap.AddRootNamespaces(Xlmns)
soap.AddAction()
//Auth Handling WS-Username token
if dev.params.Username != "" && dev.params.Password != "" && dev.params.UseWSUsernameToken == 1 {
soap.AddWSSecurity(dev.params.Username, dev.params.Password)
}
return networking.SendSoap(dev.params.HttpClient, endpoint, soap.String(), dev.params.Username, dev.params.Password, "")
}
//CallMethod functions call an method, defined <method> struct with authentication data
func (dev Device) callMethodDoEx(endpoint string, method interface{}) (networking.RestResponse, string) {
output, err := xml.MarshalIndent(method, " ", " ")
if err != nil {
return networking.NewRestResponse(nil, -1, err), ""
}
soap, err := dev.buildMethodSOAP(string(output))
if err != nil {
return networking.NewRestResponse(nil, -1, err), ""
}
soap.AddRootNamespaces(Xlmns)
soap.AddAction()
//Auth Handling WS-Username token
if dev.params.Username != "" && dev.params.Password != "" && dev.params.UseWSUsernameToken == 1 {
soap.AddWSSecurity(dev.params.Username, dev.params.Password)
}
httpType := ""
if dev.params.HttpType == 0 {
httpType = "http"
} else {
httpType = "https"
}
resp := networking.SendSoapEx(endpoint, soap.String(), dev.params.Username, dev.params.Password, httpType, dev.params.EncryptionType)
return resp, soap.String()
}
Go
1
https://gitee.com/dzhw0314/onvif.git
git@gitee.com:dzhw0314/onvif.git
dzhw0314
onvif
onvif
master

搜索帮助