From 7db86a1c70609e7e209135bddc00caa0a79d1f61 Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Thu, 29 Feb 2024 23:10:41 +0100 Subject: [PATCH] support enum values in binary, hex and exponential format (#93) --- pkg/conversion/conversion.go | 83 ++++++++++++++++++----- pkg/conversion/conversion_test.go | 109 +++++++++++++++++++++++++++++- pkg/conversion/definition.go | 4 +- 3 files changed, 174 insertions(+), 22 deletions(-) diff --git a/pkg/conversion/conversion.go b/pkg/conversion/conversion.go index 896a519e1..77005d661 100644 --- a/pkg/conversion/conversion.go +++ b/pkg/conversion/conversion.go @@ -5,7 +5,6 @@ import ( "bytes" "fmt" "io" - "math" "net/http" "net/url" "os" @@ -68,13 +67,13 @@ import ( type {{ .Enum.Name }} = {{ .Enum.DefName }}.{{ .Enum.Name }} const ( - {{- $en := .Enum }} - {{- range .Enum.Values }} - {{- range .Description }} +{{- $en := .Enum }} +{{- range .Enum.Values }} +{{- range .Description }} // {{ . }} - {{- end }} +{{- end }} {{ .Name }} {{ $en.Name }} = {{ $en.DefName }}.{{ .Name }} - {{- end }} +{{- end }} ) {{- else }} @@ -90,7 +89,7 @@ import ( {{- range .Enum.Description }} // {{ . }} {{- end }} -type {{ .Enum.Name }} uint32 +type {{ .Enum.Name }} uint64 const ( {{- $pn := .Enum.Name }} @@ -264,8 +263,24 @@ func parseDescription(in string) []string { return lines } +func uintPow(base, exp uint64) uint64 { + result := uint64(1) + for { + if exp&1 == 1 { + result *= base + } + exp >>= 1 + if exp == 0 { + break + } + base *= base + } + + return result +} + type outEnumValue struct { - Value uint32 + Value uint64 Name string Description []string } @@ -357,19 +372,51 @@ func processDefinition( Bitmask: enum.Bitmask, } - for _, val := range enum.Values { - tmp, err := strconv.ParseInt(val.Value, 10, 64) - if err != nil { - return nil, err - } - if tmp < 0 || tmp > int64(math.Pow(2, 32)) { - return nil, fmt.Errorf("enum values that overflow an uint32 are not supported") + for _, entry := range enum.Entries { + var v uint64 + + switch { + case strings.HasPrefix(entry.Value, "0b"): + tmp, err := strconv.ParseUint(entry.Value[2:], 2, 64) + if err != nil { + return nil, err + } + v = tmp + + case strings.HasPrefix(entry.Value, "0x"): + tmp, err := strconv.ParseUint(entry.Value[2:], 16, 64) + if err != nil { + return nil, err + } + v = tmp + + case strings.Contains(entry.Value, "**"): + parts := strings.SplitN(entry.Value, "**", 2) + + x, err := strconv.ParseUint(parts[0], 10, 64) + if err != nil { + return nil, err + } + + y, err := strconv.ParseUint(parts[1], 10, 64) + if err != nil { + return nil, err + } + + v = uintPow(x, y) + + default: + tmp, err := strconv.ParseUint(entry.Value, 10, 64) + if err != nil { + return nil, err + } + v = tmp } oute.Values = append(oute.Values, &outEnumValue{ - Value: uint32(tmp), - Name: val.Name, - Description: parseDescription(val.Description), + Value: v, + Name: entry.Name, + Description: parseDescription(entry.Description), }) } diff --git a/pkg/conversion/conversion_test.go b/pkg/conversion/conversion_test.go index 1e62b39c6..5f02eaf1f 100644 --- a/pkg/conversion/conversion_test.go +++ b/pkg/conversion/conversion_test.go @@ -29,6 +29,14 @@ const testDialect = ` E + + + + + + + + @@ -44,9 +52,10 @@ const testDialect = ` ` -var testDialectGo = `//autogenerated:yes +var testMessageGo = `//autogenerated:yes //nolint:revive,misspell,govet,lll package testdialect + // Detected anomaly info measured by onboard sensors and actuators. type MessageAMessage struct { // a test uint8 @@ -65,6 +74,98 @@ func (*MessageAMessage) GetID() uint32 { } ` +var testEnumGo = `//autogenerated:yes +//nolint:revive,misspell,govet,lll,dupl,gocritic +package testdialect + +import ( + "strconv" + "fmt" +) + +// Detected Anomaly Types. +type A_TYPE uint64 + +const ( + // A. + A A_TYPE = 0 + // B. + B A_TYPE = 1 + // C. + C A_TYPE = 2 + // D. + D A_TYPE = 3 + // E + E A_TYPE = 4 + BIT0 A_TYPE = 1 + BIT4 A_TYPE = 16 + BIT8 A_TYPE = 256 + BIT16 A_TYPE = 65536 + BIT60 A_TYPE = 1152921504606846976 + BIT61 A_TYPE = 2305843009213693952 + BIT62 A_TYPE = 4611686018427387904 + BIT63 A_TYPE = 9223372036854775808 +) + +var labels_A_TYPE = map[A_TYPE]string{ + A: "A", + B: "B", + C: "C", + D: "D", + E: "E", + BIT0: "BIT0", + BIT4: "BIT4", + BIT8: "BIT8", + BIT16: "BIT16", + BIT60: "BIT60", + BIT61: "BIT61", + BIT62: "BIT62", + BIT63: "BIT63", +} + +var values_A_TYPE = map[string]A_TYPE{ + "A": A, + "B": B, + "C": C, + "D": D, + "E": E, + "BIT0": BIT0, + "BIT4": BIT4, + "BIT8": BIT8, + "BIT16": BIT16, + "BIT60": BIT60, + "BIT61": BIT61, + "BIT62": BIT62, + "BIT63": BIT63, +} + +// MarshalText implements the encoding.TextMarshaler interface. +func (e A_TYPE) MarshalText() ([]byte, error) { + if name, ok := labels_A_TYPE[e]; ok { + return []byte(name), nil + } + return []byte(strconv.Itoa(int(e))), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (e *A_TYPE) UnmarshalText(text []byte) error { + if value, ok := values_A_TYPE[string(text)]; ok { + *e = value + } else if value, err := strconv.Atoi(string(text)); err == nil { + *e = A_TYPE(value) + } else { + return fmt.Errorf("invalid label '%s'", text) + } + return nil +} + +// String implements the fmt.Stringer interface. +func (e A_TYPE) String() string { + val, _ := e.MarshalText() + return string(val) +} +` + func TestConversion(t *testing.T) { dir, err := os.MkdirTemp("", "gomavlib") require.NoError(t, err) @@ -80,5 +181,9 @@ func TestConversion(t *testing.T) { buf, err := os.ReadFile("testdialect/message_a_message.go") require.NoError(t, err) - require.Equal(t, testDialectGo, string(buf)) + require.Equal(t, testMessageGo, string(buf)) + + buf, err = os.ReadFile("testdialect/enum_a_type.go") + require.NoError(t, err) + require.Equal(t, testEnumGo, string(buf)) } diff --git a/pkg/conversion/definition.go b/pkg/conversion/definition.go index 75750881d..4395cc700 100644 --- a/pkg/conversion/definition.go +++ b/pkg/conversion/definition.go @@ -5,7 +5,7 @@ import ( "strconv" ) -type definitionEnumValue struct { +type definitionEnumEntry struct { Value string `xml:"value,attr"` Name string `xml:"name,attr"` Description string `xml:"description"` @@ -14,7 +14,7 @@ type definitionEnumValue struct { type definitionEnum struct { Name string `xml:"name,attr"` Description string `xml:"description"` - Values []*definitionEnumValue `xml:"entry"` + Entries []*definitionEnumEntry `xml:"entry"` Bitmask bool `xml:"bitmask,attr"` }