av/revid/config/parameter/generate_parameters.go

314 lines
10 KiB
Go

// +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 <saxon@ausocean.org>
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 is autogenerated code for config parameters - DO NOT EDIT.
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)
}
{{if ne .Min .Max}}
if _v < {{.Min}} || _v > {{.Max}} {
return fmt.Errorf("invalid value %v",_v)
}
{{end}}
*{{.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)
}
{{if ne .Min .Max}}
if _v < {{.Min}} || _v > {{.Max}} {
return fmt.Errorf("invalid value %v",_v)
}
{{end}}
*{{.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)
}
{{if ne .Min .Max}}
if _v < {{.Min}} || _v > {{.Max}} {
return fmt.Errorf("invalid value %v",_v)
}
{{end}}
*{{.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 -}}
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))
}
}