Files
onvif/Device.go
yakovlevdmv 42e62d7f52 Bugs fixed
2018-04-07 22:16:11 +03:00

293 lines
7.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package goonvif
import (
"encoding/xml"
"fmt"
"github.com/beevik/etree"
"github.com/yakovlevdmv/gosoap"
"strconv"
"github.com/yakovlevdmv/WS-Discovery"
"github.com/yakovlevdmv/goonvif/networking"
"reflect"
"strings"
"github.com/yakovlevdmv/goonvif/Device"
"errors"
"net/http"
"io/ioutil"
)
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",
}
type DeviceType int
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
}
//deviceInfo struct represents an abstract ONVIF device.
//It contains methods, which helps to communicate with ONVIF device
type device struct {
xaddr string
login string
password string
endpoints map[string]string
info deviceInfo
}
func (dev *device)GetServices() map[string]string {
return dev.endpoints
}
func GetAvailableDevicesAtSpecificEthernetInterface(interfaceName string) []device {
/*
Call an WS-Discovery Probe Message to Discover NVT type Devices
*/
devices := WS_Discovery.SendProbe(interfaceName, nil, []string{"dn:"+NVT.String()}, map[string]string{"dn":"http://www.onvif.org/ver10/network/wsdl"})
//nvtDevices := make([]device, len(devices))
for _, j := range devices {
fmt.Println(j)
//nvtDevices[i] = NewDevice()
}
return nil
}
func (dev *device) getSupportedServices() {
resp, err := dev.CallMethod(Device.GetCapabilities{Category:"All"})
if err != nil {
//log.Println(err.Error())
return
} else {
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{
//fmt.Println(j.Text())
//fmt.Println(j.Parent().Tag)
dev.addEndpoint(j.Parent().Tag, j.Text())
}
}
}
//NewDevice function construct a ONVIF Device entity
func NewDevice(xaddr string) (*device, error) {
dev := new(device)
dev.xaddr = xaddr
dev.endpoints = make(map[string]string)
dev.addEndpoint("Device", "http://"+xaddr+"/onvif/device_service")
systemDateTime := Device.GetSystemDateAndTime{}
resp, err := dev.CallMethod(systemDateTime)
if err != nil || resp.StatusCode != http.StatusOK {
//panic(errors.New("camera is not available at " + xaddr + " or it does not support ONVIF services"))
return nil, errors.New("camera is not available at " + xaddr + " or it does not support ONVIF services")
}
dev.getSupportedServices()
return dev, nil
}
func (dev *device)addEndpoint(Key, Value string) {
dev.endpoints[Key]=Value
}
//Authenticate function authenticate client in the ONVIF Device.
//Function takes <username> and <password> params.
//You should use this function to allow authorized requests to the ONVIF Device
//To change auth data call this function again.
func (dev *device) Authenticate(username, password string) {
dev.login = username
dev.password = password
}
func 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)
soap.AddRootNamespace("onvif", "http://www.onvif.org/ver10/device/wsdl")
return soap, nil
}
//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 := pkgPath[len(pkgPath)-1]
var endpoint string
switch pkg {
case "Device": endpoint = dev.endpoints["Device"]
case "Event": endpoint = dev.endpoints["Event"]
case "Imaging": endpoint = dev.endpoints["Imaging"]
case "Media": endpoint = dev.endpoints["Media"]
case "PTZ": endpoint = dev.endpoints["PTZ"]
}
//fmt.Println("endpoint", endpoint)
//TODO: Get endpoint automatically
if dev.login != "" && dev.password != "" {
/*resp, err := dev.сallAuthorizedMethod(endpoint, method)
if err != nil {
panic(err)
return resp, err
}
return resp, err*/
return dev.callAuthorizedMethod(endpoint, method)
} else {
/*resp, err := dev.сallAuthorizedMethod(endpoint, method)
if err != nil {
panic(err)
return resp, err
}
return resp, err*/
return dev.callNonAuthorizedMethod(endpoint, method)
}
}
//CallNonAuthorizedMethod functions call an method, defined <method> struct without authentication data
func (dev device) callNonAuthorizedMethod(endpoint string, method interface{}) (*http.Response, error) {
//TODO: Get endpoint automatically
/*
Converting <method> struct to xml string representation
*/
output, err := xml.MarshalIndent(method, " ", " ")
if err != nil {
//log.Printf("error: %v\n", err.Error())
return nil, err
}
/*
Build an SOAP request with <method>
*/
soap, err := buildMethodSOAP(string(output))
if err != nil {
//log.Printf("error: %v\n", err)
return nil, err
}
soap.AddRootNamespaces(xlmns)
/*
Sending request and returns the response
*/
return networking.SendSoap(endpoint, soap.String())
}
//CallMethod functions call an method, defined <method> struct with authentication data
func (dev device) callAuthorizedMethod(endpoint string, method interface{}) (*http.Response, error) {
/*
Converting <method> struct to xml string representation
*/
output, err := xml.MarshalIndent(method, " ", " ")
if err != nil {
//log.Printf("error: %v\n", err.Error())
return nil, err
}
/*
Build an SOAP request with <method>
*/
soap, err := buildMethodSOAP(string(output))
if err != nil {
//log.Printf("error: %v\n", err.Error())
return nil, err
}
/*
Getting an WS-Security struct representation
*/
auth := newSecurity(dev.login, dev.password)
/*
Adding WS-Security namespaces to root element of SOAP message
*/
soap.AddRootNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext1.0.xsd")
soap.AddRootNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility1.0.xsd")
soap.AddRootNamespaces(xlmns)
soapReq, err := xml.MarshalIndent(auth, "", " ")
if err != nil {
//log.Printf("error: %v\n", err.Error())
return nil, err
}
/*
Adding WS-Security struct to SOAP header
*/
soap.AddStringHeaderContent(string(soapReq))
/*
Sending request and returns the response
*/
return networking.SendSoap(endpoint, soap.String())
}