2014-06-07 19:07:54 +00:00
// +build gotask
package gotasks
2013-10-06 21:06:47 +00:00
import (
"archive/zip"
"encoding/xml"
"fmt"
"io"
"log"
2015-06-07 08:01:20 +00:00
"net/http"
2013-10-06 21:06:47 +00:00
"os"
"path"
"path/filepath"
"regexp"
"strings"
2014-06-07 19:07:54 +00:00
"text/template"
2013-10-06 21:06:47 +00:00
"github.com/huin/goupnp"
"github.com/huin/goupnp/scpd"
2014-01-12 01:10:05 +00:00
"github.com/huin/goutil/codegen"
2014-06-07 19:07:54 +00:00
"github.com/jingweno/gotask/tasking"
2013-10-06 21:06:47 +00:00
)
var (
deviceURNPrefix = "urn:schemas-upnp-org:device:"
serviceURNPrefix = "urn:schemas-upnp-org:service:"
)
2015-06-07 08:01:20 +00:00
// DCP contains extra metadata to use when generating DCP source files.
type DCPMetadata struct {
Name string // What to name the Go DCP package.
OfficialName string // Official name for the DCP.
2017-09-01 11:42:20 +00:00
DocURL string // Optional - URL for further documentation about the DCP.
2015-06-07 08:01:20 +00:00
XMLSpecURL string // Where to download the XML spec from.
2015-06-07 08:27:13 +00:00
// Any special-case functions to run against the DCP before writing it out.
Hacks [ ] DCPHackFn
2015-06-07 08:01:20 +00:00
}
var dcpMetadata = [ ] DCPMetadata {
{
Name : "internetgateway1" ,
OfficialName : "Internet Gateway Device v1" ,
DocURL : "http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf" ,
XMLSpecURL : "http://upnp.org/specs/gw/UPnP-gw-IGD-TestFiles-20010921.zip" ,
2017-10-03 09:43:22 +00:00
Hacks : [ ] DCPHackFn {
func ( dcp * DCP ) error {
for _ , service := range dcp . Services {
if service . URN == "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" {
variables := service . SCPD . StateVariables
for key , variable := range variables {
varName := variable . Name
if varName == "TotalBytesSent" || varName == "TotalBytesReceived" {
// Fix size of total bytes which is by default ui4 or maximum 4 GiB.
variable . DataType . Name = "ui8"
variables [ key ] = variable
}
}
break
}
}
return nil
} ,
} ,
2015-06-07 08:01:20 +00:00
} ,
{
Name : "internetgateway2" ,
OfficialName : "Internet Gateway Device v2" ,
DocURL : "http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v2-Device.pdf" ,
XMLSpecURL : "http://upnp.org/specs/gw/UPnP-gw-IGD-Testfiles-20110224.zip" ,
2015-06-07 08:27:13 +00:00
Hacks : [ ] DCPHackFn {
func ( dcp * DCP ) error {
missingURN := "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1"
if _ , ok := dcp . ServiceTypes [ missingURN ] ; ok {
return nil
}
urnParts , err := extractURNParts ( missingURN , serviceURNPrefix )
if err != nil {
return err
}
dcp . ServiceTypes [ missingURN ] = urnParts
return nil
} ,
} ,
2015-06-07 08:01:20 +00:00
} ,
{
Name : "av1" ,
OfficialName : "MediaServer v1 and MediaRenderer v1" ,
DocURL : "http://upnp.org/specs/av/av1/" ,
XMLSpecURL : "http://upnp.org/specs/av/UPnP-av-TestFiles-20070927.zip" ,
} ,
}
2015-06-07 08:27:13 +00:00
type DCPHackFn func ( * DCP ) error
2014-06-07 19:07:54 +00:00
// NAME
// specgen - generates Go code from the UPnP specification files.
//
// DESCRIPTION
// The specification is available for download from:
//
// OPTIONS
2015-06-07 08:01:20 +00:00
// -s, --specs_dir=<spec directory>
// Path to the specification storage directory. This is used to find (and download if not present) the specification ZIP files. Defaults to 'specs'
2014-06-07 19:07:54 +00:00
// -o, --out_dir=<output directory>
2015-06-07 08:01:20 +00:00
// Path to the output directory. This is is where the DCP source files will be placed. Should normally correspond to the directory for github.com/huin/goupnp/dcps. Defaults to '../dcps'
2014-06-07 19:07:54 +00:00
// --nogofmt
// Disable passing the output through gofmt. Do this if debugging code output problems and needing to see the generated code prior to being passed through gofmt.
func TaskSpecgen ( t * tasking . T ) {
2015-06-07 08:01:20 +00:00
specsDir := fallbackStrValue ( "specs" , t . Flags . String ( "specs_dir" ) , t . Flags . String ( "s" ) )
if err := os . MkdirAll ( specsDir , os . ModePerm ) ; err != nil {
t . Fatalf ( "Could not create specs-dir %q: %v\n" , specsDir , err )
2014-06-07 19:07:54 +00:00
}
2015-06-07 08:01:20 +00:00
outDir := fallbackStrValue ( "../dcps" , t . Flags . String ( "out_dir" ) , t . Flags . String ( "o" ) )
2014-06-07 19:07:54 +00:00
useGofmt := ! t . Flags . Bool ( "nogofmt" )
2015-06-07 08:27:13 +00:00
NEXT_DCP :
2015-06-07 08:01:20 +00:00
for _ , d := range dcpMetadata {
specFilename := filepath . Join ( specsDir , d . Name + ".zip" )
err := acquireFile ( specFilename , d . XMLSpecURL )
if err != nil {
t . Logf ( "Could not acquire spec for %s, skipping: %v\n" , d . Name , err )
2015-06-07 08:27:13 +00:00
continue NEXT_DCP
2013-10-06 21:06:47 +00:00
}
2015-06-07 08:01:20 +00:00
dcp := newDCP ( d )
if err := dcp . processZipFile ( specFilename ) ; err != nil {
log . Printf ( "Error processing spec for %s in file %q: %v" , d . Name , specFilename , err )
2015-06-07 08:27:13 +00:00
continue NEXT_DCP
}
for i , hack := range d . Hacks {
if err := hack ( dcp ) ; err != nil {
log . Printf ( "Error with Hack[%d] for %s: %v" , i , d . Name , err )
continue NEXT_DCP
}
2013-10-06 21:06:47 +00:00
}
2015-06-07 08:01:20 +00:00
dcp . writePackage ( outDir , useGofmt )
2014-06-07 19:07:54 +00:00
if err := dcp . writePackage ( outDir , useGofmt ) ; err != nil {
2014-06-07 19:29:11 +00:00
log . Printf ( "Error writing package %q: %v" , dcp . Metadata . Name , err )
2015-06-07 08:27:13 +00:00
continue NEXT_DCP
2013-10-06 21:06:47 +00:00
}
}
}
2015-06-07 08:01:20 +00:00
func fallbackStrValue ( defaultValue string , values ... string ) string {
for _ , v := range values {
if v != "" {
return v
}
}
return defaultValue
2014-06-07 19:29:11 +00:00
}
2015-06-07 08:01:20 +00:00
func acquireFile ( specFilename string , xmlSpecURL string ) error {
if f , err := os . Open ( specFilename ) ; err != nil {
if ! os . IsNotExist ( err ) {
return err
}
} else {
f . Close ( )
return nil
}
2014-06-07 19:55:03 +00:00
2015-06-07 08:01:20 +00:00
resp , err := http . Get ( xmlSpecURL )
if err != nil {
return err
}
defer resp . Body . Close ( )
2013-10-06 21:06:47 +00:00
2015-06-07 08:01:20 +00:00
if resp . StatusCode != http . StatusOK {
return fmt . Errorf ( "could not download spec %q from %q: " ,
specFilename , xmlSpecURL , resp . Status )
2013-10-06 21:06:47 +00:00
}
2015-06-07 08:01:20 +00:00
tmpFilename := specFilename + ".download"
w , err := os . Create ( tmpFilename )
if err != nil {
return err
2013-10-06 21:06:47 +00:00
}
2015-06-07 08:01:20 +00:00
defer w . Close ( )
2013-10-06 21:06:47 +00:00
2015-06-07 08:01:20 +00:00
_ , err = io . Copy ( w , resp . Body )
if err != nil {
return err
2013-10-06 21:06:47 +00:00
}
2015-06-07 08:01:20 +00:00
return os . Rename ( tmpFilename , specFilename )
2013-10-06 21:06:47 +00:00
}
// DCP collects together information about a UPnP Device Control Protocol.
type DCP struct {
2014-06-07 19:34:27 +00:00
Metadata DCPMetadata
2013-10-06 21:06:47 +00:00
DeviceTypes map [ string ] * URNParts
ServiceTypes map [ string ] * URNParts
Services [ ] SCPDWithURN
}
2014-06-07 19:34:27 +00:00
func newDCP ( metadata DCPMetadata ) * DCP {
2013-10-06 21:06:47 +00:00
return & DCP {
2014-06-07 19:29:11 +00:00
Metadata : metadata ,
2013-10-06 21:06:47 +00:00
DeviceTypes : make ( map [ string ] * URNParts ) ,
ServiceTypes : make ( map [ string ] * URNParts ) ,
}
}
2015-06-07 08:01:20 +00:00
func ( dcp * DCP ) processZipFile ( filename string ) error {
archive , err := zip . OpenReader ( filename )
2013-10-06 21:06:47 +00:00
if err != nil {
2015-06-07 08:01:20 +00:00
return fmt . Errorf ( "error reading zip file %q: %v" , filename , err )
2013-10-06 21:06:47 +00:00
}
2015-06-07 08:01:20 +00:00
defer archive . Close ( )
2013-10-06 21:06:47 +00:00
for _ , deviceFile := range globFiles ( "*/device/*.xml" , archive ) {
2015-06-07 08:01:20 +00:00
if err := dcp . processDeviceFile ( deviceFile ) ; err != nil {
return err
}
2013-10-06 21:06:47 +00:00
}
for _ , scpdFile := range globFiles ( "*/service/*.xml" , archive ) {
2015-06-07 08:01:20 +00:00
if err := dcp . processSCPDFile ( scpdFile ) ; err != nil {
return err
}
2013-10-06 21:06:47 +00:00
}
2015-06-07 08:01:20 +00:00
return nil
2013-10-06 21:06:47 +00:00
}
2015-06-07 08:01:20 +00:00
func ( dcp * DCP ) processDeviceFile ( file * zip . File ) error {
2013-10-06 21:06:47 +00:00
var device goupnp . Device
if err := unmarshalXmlFile ( file , & device ) ; err != nil {
2015-06-07 08:01:20 +00:00
return fmt . Errorf ( "error decoding device XML from file %q: %v" , file . Name , err )
2013-10-06 21:06:47 +00:00
}
2015-06-07 08:01:20 +00:00
var mainErr error
2013-10-06 21:06:47 +00:00
device . VisitDevices ( func ( d * goupnp . Device ) {
t := strings . TrimSpace ( d . DeviceType )
if t != "" {
u , err := extractURNParts ( t , deviceURNPrefix )
if err != nil {
2015-06-07 08:01:20 +00:00
mainErr = err
2013-10-06 21:06:47 +00:00
}
dcp . DeviceTypes [ t ] = u
}
} )
device . VisitServices ( func ( s * goupnp . Service ) {
u , err := extractURNParts ( s . ServiceType , serviceURNPrefix )
if err != nil {
2015-06-07 08:01:20 +00:00
mainErr = err
2013-10-06 21:06:47 +00:00
}
dcp . ServiceTypes [ s . ServiceType ] = u
} )
2015-06-07 08:01:20 +00:00
return mainErr
2013-10-06 21:06:47 +00:00
}
2014-06-07 19:07:54 +00:00
func ( dcp * DCP ) writePackage ( outDir string , useGofmt bool ) error {
2014-06-07 19:29:11 +00:00
packageDirname := filepath . Join ( outDir , dcp . Metadata . Name )
2013-10-06 21:06:47 +00:00
err := os . MkdirAll ( packageDirname , os . ModePerm )
if err != nil && ! os . IsExist ( err ) {
return err
}
2014-06-07 19:29:11 +00:00
packageFilename := filepath . Join ( packageDirname , dcp . Metadata . Name + ".go" )
2013-10-06 21:06:47 +00:00
packageFile , err := os . Create ( packageFilename )
if err != nil {
return err
}
2014-01-06 19:47:57 +00:00
var output io . WriteCloser = packageFile
2014-06-07 19:07:54 +00:00
if useGofmt {
2014-01-12 01:10:05 +00:00
if output , err = codegen . NewGofmtWriteCloser ( output ) ; err != nil {
2014-01-06 19:47:57 +00:00
packageFile . Close ( )
return err
}
}
if err = packageTmpl . Execute ( output , dcp ) ; err != nil {
output . Close ( )
return err
}
return output . Close ( )
}
2015-06-07 08:01:20 +00:00
func ( dcp * DCP ) processSCPDFile ( file * zip . File ) error {
2013-10-06 21:06:47 +00:00
scpd := new ( scpd . SCPD )
if err := unmarshalXmlFile ( file , scpd ) ; err != nil {
2015-06-07 08:01:20 +00:00
return fmt . Errorf ( "error decoding SCPD XML from file %q: %v" , file . Name , err )
2013-10-06 21:06:47 +00:00
}
scpd . Clean ( )
urnParts , err := urnPartsFromSCPDFilename ( file . Name )
if err != nil {
2015-06-07 08:01:20 +00:00
return fmt . Errorf ( "could not recognize SCPD filename %q: %v" , file . Name , err )
2013-10-06 21:06:47 +00:00
}
dcp . Services = append ( dcp . Services , SCPDWithURN {
URNParts : urnParts ,
SCPD : scpd ,
} )
2015-06-07 08:01:20 +00:00
return nil
2013-10-06 21:06:47 +00:00
}
type SCPDWithURN struct {
* URNParts
SCPD * scpd . SCPD
}
2015-06-07 09:42:22 +00:00
func ( s * SCPDWithURN ) WrapArguments ( args [ ] * scpd . Argument ) ( argumentWrapperList , error ) {
wrappedArgs := make ( argumentWrapperList , len ( args ) )
for i , arg := range args {
wa , err := s . wrapArgument ( arg )
if err != nil {
return nil , err
}
wrappedArgs [ i ] = wa
}
return wrappedArgs , nil
}
func ( s * SCPDWithURN ) wrapArgument ( arg * scpd . Argument ) ( * argumentWrapper , error ) {
2013-12-31 16:56:20 +00:00
relVar := s . SCPD . GetStateVariable ( arg . RelatedStateVariable )
if relVar == nil {
return nil , fmt . Errorf ( "no such state variable: %q, for argument %q" , arg . RelatedStateVariable , arg . Name )
}
cnv , ok := typeConvs [ relVar . DataType . Name ]
if ! ok {
return nil , fmt . Errorf ( "unknown data type: %q, for state variable %q, for argument %q" , relVar . DataType . Type , arg . RelatedStateVariable , arg . Name )
}
return & argumentWrapper {
2015-06-07 09:42:22 +00:00
Argument : * arg ,
2013-12-31 16:56:20 +00:00
relVar : relVar ,
conv : cnv ,
} , nil
}
type argumentWrapper struct {
scpd . Argument
relVar * scpd . StateVariable
conv conv
}
func ( arg * argumentWrapper ) AsParameter ( ) string {
return fmt . Sprintf ( "%s %s" , arg . Name , arg . conv . ExtType )
}
2015-06-07 09:42:22 +00:00
func ( arg * argumentWrapper ) HasDoc ( ) bool {
rng := arg . relVar . AllowedValueRange
return ( ( rng != nil && ( rng . Minimum != "" || rng . Maximum != "" || rng . Step != "" ) ) ||
len ( arg . relVar . AllowedValues ) > 0 )
}
2013-12-31 17:52:43 +00:00
func ( arg * argumentWrapper ) Document ( ) string {
relVar := arg . relVar
if rng := relVar . AllowedValueRange ; rng != nil {
var parts [ ] string
if rng . Minimum != "" {
parts = append ( parts , fmt . Sprintf ( "minimum=%s" , rng . Minimum ) )
}
if rng . Maximum != "" {
parts = append ( parts , fmt . Sprintf ( "maximum=%s" , rng . Maximum ) )
}
if rng . Step != "" {
parts = append ( parts , fmt . Sprintf ( "step=%s" , rng . Step ) )
}
return "allowed value range: " + strings . Join ( parts , ", " )
}
if len ( relVar . AllowedValues ) != 0 {
return "allowed values: " + strings . Join ( relVar . AllowedValues , ", " )
}
return ""
}
2013-12-31 16:56:20 +00:00
func ( arg * argumentWrapper ) Marshal ( ) string {
return fmt . Sprintf ( "soap.Marshal%s(%s)" , arg . conv . FuncSuffix , arg . Name )
}
func ( arg * argumentWrapper ) Unmarshal ( objVar string ) string {
return fmt . Sprintf ( "soap.Unmarshal%s(%s.%s)" , arg . conv . FuncSuffix , objVar , arg . Name )
}
2015-06-07 09:42:22 +00:00
type argumentWrapperList [ ] * argumentWrapper
func ( args argumentWrapperList ) HasDoc ( ) bool {
for _ , arg := range args {
if arg . HasDoc ( ) {
return true
}
}
return false
}
2013-12-31 16:56:20 +00:00
type conv struct {
FuncSuffix string
ExtType string
}
// typeConvs maps from a SOAP type (e.g "fixed.14.4") to the function name
// suffix inside the soap module (e.g "Fixed14_4") and the Go type.
var typeConvs = map [ string ] conv {
"ui1" : conv { "Ui1" , "uint8" } ,
"ui2" : conv { "Ui2" , "uint16" } ,
"ui4" : conv { "Ui4" , "uint32" } ,
2017-10-03 09:43:22 +00:00
"ui8" : conv { "Ui8" , "uint64" } ,
2013-12-31 16:56:20 +00:00
"i1" : conv { "I1" , "int8" } ,
"i2" : conv { "I2" , "int16" } ,
"i4" : conv { "I4" , "int32" } ,
"int" : conv { "Int" , "int64" } ,
"r4" : conv { "R4" , "float32" } ,
"r8" : conv { "R8" , "float64" } ,
"number" : conv { "R8" , "float64" } , // Alias for r8.
"fixed.14.4" : conv { "Fixed14_4" , "float64" } ,
"float" : conv { "R8" , "float64" } ,
"char" : conv { "Char" , "rune" } ,
"string" : conv { "String" , "string" } ,
"date" : conv { "Date" , "time.Time" } ,
"dateTime" : conv { "DateTime" , "time.Time" } ,
"dateTime.tz" : conv { "DateTimeTz" , "time.Time" } ,
"time" : conv { "TimeOfDay" , "soap.TimeOfDay" } ,
"time.tz" : conv { "TimeOfDayTz" , "soap.TimeOfDay" } ,
"boolean" : conv { "Boolean" , "bool" } ,
"bin.base64" : conv { "BinBase64" , "[]byte" } ,
"bin.hex" : conv { "BinHex" , "[]byte" } ,
2015-06-06 23:54:08 +00:00
"uri" : conv { "URI" , "*url.URL" } ,
2013-12-31 16:56:20 +00:00
}
2015-06-07 08:01:20 +00:00
func globFiles ( pattern string , archive * zip . ReadCloser ) [ ] * zip . File {
2013-10-06 21:06:47 +00:00
var files [ ] * zip . File
for _ , f := range archive . File {
if matched , err := path . Match ( pattern , f . Name ) ; err != nil {
// This shouldn't happen - all patterns are hard-coded, errors in them
// are a programming error.
panic ( err )
} else if matched {
files = append ( files , f )
}
}
return files
}
func unmarshalXmlFile ( file * zip . File , data interface { } ) error {
r , err := file . Open ( )
if err != nil {
return err
}
decoder := xml . NewDecoder ( r )
2016-10-25 16:18:49 +00:00
defer r . Close ( )
2013-10-06 21:06:47 +00:00
return decoder . Decode ( data )
}
type URNParts struct {
URN string
Name string
Version string
}
func ( u * URNParts ) Const ( ) string {
return fmt . Sprintf ( "URN_%s_%s" , u . Name , u . Version )
}
// extractURNParts extracts the name and version from a URN string.
func extractURNParts ( urn , expectedPrefix string ) ( * URNParts , error ) {
if ! strings . HasPrefix ( urn , expectedPrefix ) {
return nil , fmt . Errorf ( "%q does not have expected prefix %q" , urn , expectedPrefix )
}
parts := strings . SplitN ( strings . TrimPrefix ( urn , expectedPrefix ) , ":" , 2 )
if len ( parts ) != 2 {
return nil , fmt . Errorf ( "%q does not have a name and version" , urn )
}
name , version := parts [ 0 ] , parts [ 1 ]
return & URNParts { urn , name , version } , nil
}
var scpdFilenameRe = regexp . MustCompile (
` .*/([a-zA-Z0-9]+)([0-9]+)\.xml ` )
func urnPartsFromSCPDFilename ( filename string ) ( * URNParts , error ) {
parts := scpdFilenameRe . FindStringSubmatch ( filename )
if len ( parts ) != 3 {
return nil , fmt . Errorf ( "SCPD filename %q does not have expected number of parts" , filename )
}
name , version := parts [ 1 ] , parts [ 2 ]
return & URNParts {
URN : serviceURNPrefix + name + ":" + version ,
Name : name ,
Version : version ,
} , nil
}
2014-06-07 19:07:54 +00:00
2014-06-07 19:29:11 +00:00
var packageTmpl = template . Must ( template . New ( "package" ) . Parse ( ` { { $ name := . Metadata . Name } }
2014-06-07 20:24:11 +00:00
// Client for UPnP Device Control Protocol {{.Metadata.OfficialName}}.
// {{if .Metadata.DocURL}}
// This DCP is documented in detail at: {{.Metadata.DocURL}}{{end}}
//
2015-06-06 10:53:50 +00:00
// Typically, use one of the New* functions to create clients for services.
2014-06-07 19:29:11 +00:00
package { { $ name } }
2014-06-07 19:07:54 +00:00
2017-11-05 13:40:23 +00:00
// ***********************************************************
// GENERATED FILE - DO NOT EDIT BY HAND. See README.md
// ***********************************************************
2014-06-07 19:29:41 +00:00
2014-06-07 19:07:54 +00:00
import (
2015-06-06 10:53:50 +00:00
"net/url"
2014-06-07 19:07:54 +00:00
"time"
"github.com/huin/goupnp"
"github.com/huin/goupnp/soap"
)
// Hack to avoid Go complaining if time isn't used.
var _ time . Time
// Device URNs:
const ( { { range . DeviceTypes } }
{ { . Const } } = "{{.URN}}" { { end } }
)
// Service URNs:
const ( { { range . ServiceTypes } }
{ { . Const } } = "{{.URN}}" { { end } }
)
{ { range . Services } }
{ { $ srv := . } }
{ { $ srvIdent := printf "%s%s" . Name . Version } }
// {{$srvIdent}} is a client for UPnP SOAP service with URN "{{.URN}}". See
// goupnp.ServiceClient, which contains RootDevice and Service attributes which
// are provided for informational value.
type { { $ srvIdent } } struct {
goupnp . ServiceClient
}
// New{{$srvIdent}}Clients discovers instances of the service on the network,
// and returns clients to any that are found. errors will contain an error for
// any devices that replied but which could not be queried, and err will be set
// if the discovery process failed outright.
//
// This is a typical entry calling point into this package.
func New { { $ srvIdent } } Clients ( ) ( clients [ ] * { { $ srvIdent } } , errors [ ] error , err error ) {
var genericClients [ ] goupnp . ServiceClient
if genericClients , errors , err = goupnp . NewServiceClients ( { { $ srv . Const } } ) ; err != nil {
return
}
2015-06-06 10:53:50 +00:00
clients = new { { $ srvIdent } } ClientsFromGenericClients ( genericClients )
return
}
// New{{$srvIdent}}ClientsByURL discovers instances of the service at the given
// URL, and returns clients to any that are found. An error is returned if
// there was an error probing the service.
//
// This is a typical entry calling point into this package when reusing an
// previously discovered service URL.
func New { { $ srvIdent } } ClientsByURL ( loc * url . URL ) ( [ ] * { { $ srvIdent } } , error ) {
genericClients , err := goupnp . NewServiceClientsByURL ( loc , { { $ srv . Const } } )
if err != nil {
return nil , err
}
return new { { $ srvIdent } } ClientsFromGenericClients ( genericClients ) , nil
}
// New{{$srvIdent}}ClientsFromRootDevice discovers instances of the service in
// a given root device, and returns clients to any that are found. An error is
// returned if there was not at least one instance of the service within the
// device. The location parameter is simply assigned to the Location attribute
// of the wrapped ServiceClient(s).
//
// This is a typical entry calling point into this package when reusing an
// previously discovered root device.
func New { { $ srvIdent } } ClientsFromRootDevice ( rootDevice * goupnp . RootDevice , loc * url . URL ) ( [ ] * { { $ srvIdent } } , error ) {
genericClients , err := goupnp . NewServiceClientsFromRootDevice ( rootDevice , loc , { { $ srv . Const } } )
if err != nil {
return nil , err
}
return new { { $ srvIdent } } ClientsFromGenericClients ( genericClients ) , nil
}
func new { { $ srvIdent } } ClientsFromGenericClients ( genericClients [ ] goupnp . ServiceClient ) [ ] * { { $ srvIdent } } {
clients := make ( [ ] * { { $ srvIdent } } , len ( genericClients ) )
2014-06-07 19:07:54 +00:00
for i := range genericClients {
clients [ i ] = & { { $ srvIdent } } { genericClients [ i ] }
}
2015-06-06 10:53:50 +00:00
return clients
2014-06-07 19:07:54 +00:00
}
{ { range . SCPD . Actions } } { { /* loops over *SCPDWithURN values */ } }
2015-06-07 09:42:22 +00:00
{ { $ winargs := $ srv . WrapArguments . InputArguments } }
{ { $ woutargs := $ srv . WrapArguments . OutputArguments } }
{ { if $ winargs . HasDoc } }
//
// Arguments:{{range $winargs}}{{if .HasDoc}}
2014-06-07 19:07:54 +00:00
//
2015-06-07 09:42:22 +00:00
// * {{.Name}}: {{.Document}}{{end}}{{end}}{{end}}
{ { if $ woutargs . HasDoc } }
2014-06-07 19:07:54 +00:00
//
2015-06-07 09:42:22 +00:00
// Return values:{{range $woutargs}}{{if .HasDoc}}
2014-06-07 19:07:54 +00:00
//
2015-06-07 09:42:22 +00:00
// * {{.Name}}: {{.Document}}{{end}}{{end}}{{end}}
2017-11-05 13:46:14 +00:00
func ( client * { { $ srvIdent } } ) { { . Name } } ( { { range $ winargs - } }
{ { . AsParameter } } , { { end - } }
) ( { { range $ woutargs - } }
{ { . AsParameter } } , { { end } } err error ) {
2014-06-07 19:07:54 +00:00
// Request structure.
2015-06-07 09:42:22 +00:00
request := { { if $ winargs } } & { { template "argstruct" $ winargs } } { { "{}" } } { { else } } { { "interface{}(nil)" } } { { end } }
2014-06-07 19:07:54 +00:00
// BEGIN Marshal arguments into request.
2015-06-07 09:42:22 +00:00
{ { range $ winargs } }
if request . { { . Name } } , err = { { . Marshal } } ; err != nil {
2014-06-07 19:07:54 +00:00
return
} { { end } }
// END Marshal arguments into request.
// Response structure.
2015-06-07 09:42:22 +00:00
response := { { if $ woutargs } } & { { template "argstruct" $ woutargs } } { { "{}" } } { { else } } { { "interface{}(nil)" } } { { end } }
2014-06-07 19:07:54 +00:00
// Perform the SOAP call.
if err = client . SOAPClient . PerformAction ( { { $ srv . URNParts . Const } } , "{{.Name}}" , request , response ) ; err != nil {
return
}
// BEGIN Unmarshal arguments from response.
2015-06-07 09:42:22 +00:00
{ { range $ woutargs } }
if { { . Name } } , err = { { . Unmarshal "response" } } ; err != nil {
2014-06-07 19:07:54 +00:00
return
} { { end } }
// END Unmarshal arguments from response.
return
}
2017-11-05 13:46:14 +00:00
{ { end } }
{ { end } }
2014-06-07 19:07:54 +00:00
{ { define "argstruct" } } struct { { "{" } } { { range . } }
{ { . Name } } string
{ { end } } { { "}" } } { { end } }
` ) )