// +build ignore /* DESCRIPTION generate_parameters.go uses a template to generate implementations for the Parameter interface. LICENSE Copyright (C) 2020 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. */ package main import ( "bytes" "fmt" "go/format" "math" "os" "strings" "text/template" ) // Filename to house Parameter interface implementations. const fileName = "parameters.go" // Param holds characteristics for describing Parameter interface implementations. type Param struct { // N is the name of the struct implementing the Parameter interface. N string // R is the name of the receiver for the Type and Set methods. This is set to // the lowercase of the first letter of the Parameter implementation name, N. R string // BT is the base type of a Parameter implementation. For example, for a // Parameter implementation named Bitrate, i.e. type Bitrate uint, the base // type is uint. BT string // E are the enums of a type i.e. if there is a list of identifiers we wish // to recocgnise, they are specified here. For example, a Parameter implementation // named Output may take on a value of HTTP, RTMP, RTP or File, so E is set to // []string{"HTTP","RTMP","RTP","File"} and a const list of type Output will be // generated as a result: // const ( // OutputHTTP Output = iota // OutputRTMP // OutputRTP // OutputFile // ) E []string // M, if defined, indicates a "multiple option type", which is to mean a slice // of another type that is defined. For example, if the base type, BT, is set // to a slice of a type "[]Output" (where Output is also defined in the params // list), then M would be set to "Output". The E field must be manually set to // be consistent with the Enums defined for the Output type. M string // If we wish an int, uint, or float64 value to be constrained to a particular // range then Min and Max are both set to indicate the inclusive Min and Max // possible values for a type. This will result in the generation of a range // check in the implementation's Set method. Min, Max int } // NB: Alphabetical order. var params = []Param{ {N: "AutoWhiteBalance", BT: "uint8", E: []string{"Off", "Auto", "Sun", "Cloud", "Shade", "Tungsten", "Fluorescent", "Incandescent", "Flash", "Horizon"}}, {N: "BitDepth", BT: "uint"}, // TODO(Trek): bounds. {N: "Bitrate", BT: "uint", Min: 1000, Max: 10000000}, {N: "Brightness", BT: "uint", Min: 0, Max: 100}, {N: "BurstPeriod", BT: "time.Duration"}, {N: "CBR", BT: "bool"}, {N: "CameraChan", BT: "uint8", E: []string{"Channel1", "Channel2"}}, {N: "CameraIP", BT: "string"}, {N: "Channels", BT: "uint"}, // TODO(Trek): bounds. {N: "ClipDuration", BT: "time.Duration"}, {N: "Codec", BT: "uint8", E: []string{"H264", "H265", "MJPEG", "PCM", "ADPCM"}}, {N: "Exposure", BT: "uint8", E: []string{"Auto", "Night", "NightPreview", "BackLight", "SpotLight", "Sports", "Snow", "Beach", "VeryLong", "FixedFPS", "AntiShake", "Fireworks"}}, {N: "FileFPS", BT: "uint", Min: 1, Max: 30}, {N: "Filter", BT: "uint8", E: []string{"NoOp", "MOG", "VariableFPS", "KNN", "Difference", "Basic"}}, {N: "FrameRate", BT: "uint", Min: 1, Max: 30}, {N: "HTTPAddress", BT: "string"}, {N: "Height", BT: "uint", Min: 360, Max: 1080}, {N: "HorizontalFlip", BT: "bool"}, {N: "Input", BT: "uint8", E: []string{"File", "Raspivid", "Webcam", "RTSP"}}, {N: "InputPath", BT: "string"}, {N: "Level", BT: "uint8", E: []string{"Debug", "Info", "Warning", "Error", "Fatal"}}, {N: "MinFPS", BT: "uint", Min: 1, Max: 30}, {N: "MinFrames", BT: "uint", Min: 0, Max: 1000}, {N: "Mode", BT: "uint8", E: []string{"Normal", "Paused", "Burst", "Loop"}}, {N: "MotionDownscaling", BT: "uint"}, // TODO(Scott): define bounds. {N: "MotionHistory", BT: "uint"}, // TODO(Scott/Ella): define bounds. {N: "MotionInterval", BT: "uint", Min: 0, Max: 30}, {N: "MotionKernel", BT: "uint"}, // TODO(Scott/Ella): define bounds. {N: "MotionMinArea", BT: "float64"}, // TODO(Scott/Ella): define bounds. {N: "MotionPixels", BT: "uint"}, // TODO(Scott/Ella): define bounds. {N: "MotionThreshold", BT: "float64"}, // TODO(Scott/Ella): define bounds. {N: "Output", BT: "uint8", E: []string{"HTTP", "RTMP", "RTP", "File"}}, {N: "OutputPath", BT: "string"}, {N: "Outputs", BT: "[]Output", M: "Output", E: []string{"HTTP", "RTMP", "RTP", "File"}}, {N: "PSITime", BT: "time.Duration"}, {N: "Quantization", BT: "uint"}, {N: "RBCapacity", BT: "uint", Min: 1000000, Max: 100000000}, {N: "RBMaxElements", BT: "uint", Min: 0, Max: math.MaxUint32}, {N: "RBWriteTimeout", BT: "time.Duration"}, {N: "RTMPURL", BT: "string"}, {N: "RTPAddress", BT: "string"}, {N: "RecPeriod", BT: "float64"}, // TODO(Trek): bounds. {N: "Rotation", BT: "uint", Min: 0, Max: 359}, {N: "SampleRate", BT: "uint"}, // TODO(Trek): bounds. {N: "Saturation", BT: "int", Min: -50, Max: 50}, {N: "ShowWindows", BT: "bool"}, {N: "VBRBitrate", BT: "uint", Min: 1, Max: 30}, {N: "VBRQuality", BT: "uint8", E: []string{"Standard", "Fair", "Good", "Great", "Excellent"}}, {N: "VerticalFlip", BT: "bool"}, {N: "Width", BT: "uint", Min: 640, Max: 1920}, {N: "WriteRate", BT: "float64"}, } const fileHeader = ` /* DESCRIPTION Code generated by "go run generate_parameters.go”; DO NOT EDIT. parameters.go contains implementations of the Parameter interface for all parameter types required by the configuration struct. AUTHORS Saxon Nelson-Milton LICENSE Copyright (C) 2020 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. */ package parameter import ( "fmt" "strconv" "time" "strings" ) type Parameter interface { Type() string Set(val string) error } ` const paramTemplate = ` type {{.N}} {{.BT}} {{if and .E (not .M)}} const ( {{$name := .N}} {{- range $i, $e := .E}}{{- if eq $i 0}}{{$name}}{{$e}} {{$name}} = iota{{else}}{{$name}}{{$e}}{{end}} {{end -}} ) {{end -}} {{- if .E}} func ({{.R}} *{{.N}}) Type() string { return "enum:{{range $i, $e := .E}}{{if eq $i 0}}{{$e}}{{else}},{{$e}}{{end}}{{end}}"} {{else}} func ({{.R}} *{{.N}}) Type() string { return "{{.BT}}"} {{end -}} func ({{.R}} *{{.N}}) Set(val string) error { {{- if eq .BT "string"}} *{{.R}} = {{.N}}(val) {{else if eq .BT "bool"}} switch val { case "true": *{{.R}} = true case "false": *{{.R}} = false default: return fmt.Errorf("not boolean value: %s",val) } {{else if eq .BT "int"}} _v, err := strconv.Atoi(val) if err != nil { return fmt.Errorf("could not convert set string to int: %w",err) } *{{.R}} = {{.N}}(_v) {{else if eq .BT "uint"}} _v, err := strconv.Atoi(val) if err != nil { return fmt.Errorf("could not convert set string to int: %w",err) } *{{.R}} = {{.N}}(_v) {{else if eq .BT "float64"}} _v, err := strconv.ParseFloat(val,64) if err != nil { return fmt.Errorf("could not convert set string to float: %w",err) } *{{.R}} = {{.N}}(_v) {{else if eq .BT "time.Duration"}} _v, err := strconv.Atoi(val) if err != nil { return fmt.Errorf("could not convert set string to int: %w",err) } *{{.R}} = {{.N}}(time.Duration(_v)*time.Second) {{else if .M}} vals := strings.Split(val, ",") *{{.R}} = make({{.BT}}, len(vals)) for i, v := range vals { switch v { {{- $receiver := .R}} {{- $m := .M}} {{range .E}}case "{{ . }}": (*{{$receiver}})[i] = {{$m}}{{ . }} {{end -}} default: return fmt.Errorf("unrecognised {{.N}}: %s",val) } } {{else}} switch val { {{- $receiver := .R}} {{- $name := .N}} {{range .E}}case "{{ . }}": *{{$receiver}} = {{$name}}{{ . }} {{end -}} default: return fmt.Errorf("unrecognised {{.N}}: %s",val) } {{end -}} {{- if and (or (or (eq .BT "int") (eq .BT "uint")) (eq .BT "float64")) (ne .Min .Max)}} if *{{.R}} < {{.Min}} || *{{.R}} > {{.Max}} { return fmt.Errorf("invalid value %v",*{{.R}}) } {{end -}} return nil } ` func main() { f, err := os.Create(fileName) if err != nil { panic(fmt.Sprintf("error creating %s file: %v", fileName, err)) } defer f.Close() var buf bytes.Buffer _, err = buf.Write([]byte(fileHeader)) if err != nil { panic(fmt.Sprintf("error writing header: %v", err)) } param := template.Must(template.New("param").Parse(paramTemplate)) for _, p := range params { // Use first letter of parameter name as receiver. p.R = strings.ToLower(p.N[:1]) err = param.Execute(&buf, p) if err != nil { panic(fmt.Sprintf("error executing template: %v", err)) } } b, err := format.Source(buf.Bytes()) if err != nil { f.Write(buf.Bytes()) // Useful to debug bad format. panic(fmt.Sprintf("error formatting: %v", err)) } _, err = f.Write(b) if err != nil { panic(fmt.Sprintf("error writing %s file: %v", fileName, err)) } }