From 4e93a88538a85c3345d3ed11594fe7b2668f1591 Mon Sep 17 00:00:00 2001 From: Jason Shiverick Date: Mon, 1 Mar 2021 15:45:18 -0800 Subject: [PATCH] Support canfd (#1) * Updated Message.Length to uint16 to support message lengths up to 65,535 bytes * Updated Signal.Lenght and Signal.Size to uint16 for payloads larger than 8 bytes * Added payload.go and payload_tests.go * Updated Big and Little Endian encoding methods * Added benchmarks * Added methods and tests to decode signed/unsigned signals in big/little endian * Added benchmarks for UnsignedBitsLittleEndian * Added hex string methods for Payload * Added UnmarshalPhysicalPayload and updated bit-level methods for Payload struct * Added UnmarshalValueDescriptionPayload and associated methods for Payload data struct * Added decode_tests.go * Updated decode test * Added asserts to decode_test.go * Added comparitive unit tests for can.Data and benchmarks for decoding * Updated PackLittleEndian and PackBigEndian to return new big.Int and reference pointer to payload in decode methods * Fixed units in VDM_Disconnect test Co-authored-by: Jason Shiverick Co-authored-by: Naveen Venkatesan Co-authored-by: Naveen Venkatesan --- data_test.go | 14 + frame.go | 6 +- frame_json.go | 4 +- frame_json_test.go | 2 +- internal/generate/compile.go | 6 +- payload.go | 432 +++++++++++++++++++++++++ payload_test.go | 108 +++++++ pkg/cantext/encode_test.go | 16 +- pkg/descriptor/message.go | 2 +- pkg/descriptor/signal.go | 99 ++++-- pkg/descriptor/signal_test.go | 6 +- pkg/socketcan/frame.go | 6 +- testdata/gen/go/example/example.dbc.go | 96 +++--- tests/decode_test.go | 373 +++++++++++++++++++++ 14 files changed, 1077 insertions(+), 93 deletions(-) create mode 100644 payload.go create mode 100644 payload_test.go create mode 100644 tests/decode_test.go diff --git a/data_test.go b/data_test.go index 752c2a7..63a1394 100644 --- a/data_test.go +++ b/data_test.go @@ -331,3 +331,17 @@ func BenchmarkData_PackLittleEndian(b *testing.B) { _ = data.PackLittleEndian() } } + +func BenchmarkData_UnsignedBitsBigEndian(b *testing.B) { + var data Data + for i := 0; i < b.N; i++ { + _ = data.UnsignedBitsBigEndian(0, 16) + } +} + +func BenchmarkData_UnsignedBitsLittleEndian(b *testing.B) { + var data Data + for i := 0; i < b.N; i++ { + _ = data.UnsignedBitsLittleEndian(0, 16) + } +} diff --git a/frame.go b/frame.go index 86309e2..791739f 100644 --- a/frame.go +++ b/frame.go @@ -26,7 +26,7 @@ type Frame struct { // ID is the CAN ID ID uint32 // Length is the number of bytes of data in the frame. - Length uint8 + Length uint16 // Data is the frame data. Data Data // IsRemote is true for remote frames. @@ -114,7 +114,7 @@ func (f *Frame) UnmarshalString(s string) error { if err != nil { return fmt.Errorf("invalid remote length: %v: %w", s, err) } - frame.Length = uint8(dataLength) + frame.Length = uint16(dataLength) } *f = frame return nil @@ -123,7 +123,7 @@ func (f *Frame) UnmarshalString(s string) error { if len(dataPart) > 16 || len(dataPart)%2 != 0 { return fmt.Errorf("invalid data length: %v", s) } - frame.Length = uint8(len(dataPart) / 2) + frame.Length = uint16(len(dataPart) / 2) // Parse: Data decodedData, err := hex.DecodeString(dataPart) if err != nil { diff --git a/frame_json.go b/frame_json.go index a96efca..9c46a7c 100644 --- a/frame_json.go +++ b/frame_json.go @@ -10,7 +10,7 @@ import ( type jsonFrame struct { ID uint32 `json:"id"` Data *string `json:"data"` - Length *uint8 `json:"length"` + Length *uint16 `json:"length"` Extended *bool `json:"extended"` Remote *bool `json:"remote"` } @@ -69,7 +69,7 @@ func (f *Frame) UnmarshalJSON(jsonData []byte) error { } f.Data = Data{} copy(f.Data[:], data) - f.Length = uint8(len(data)) + f.Length = uint16(len(data)) } else { f.Data = Data{} f.Length = 0 diff --git a/frame_json_test.go b/frame_json_test.go index c8c3625..256268f 100644 --- a/frame_json_test.go +++ b/frame_json_test.go @@ -102,7 +102,7 @@ func (Frame) Generate(rand *rand.Rand, size int) reflect.Value { } else { f.ID = rand.Uint32() & MaxID } - f.Length = uint8(rand.Intn(9)) + f.Length = uint16(rand.Intn(9)) if !f.IsRemote { _, _ = rand.Read(f.Data[:f.Length]) } diff --git a/internal/generate/compile.go b/internal/generate/compile.go index 6f87209..f308bbe 100644 --- a/internal/generate/compile.go +++ b/internal/generate/compile.go @@ -62,7 +62,7 @@ func (c *compiler) collectDescriptors() { Name: string(def.Name), ID: def.MessageID.ToCAN(), IsExtended: def.MessageID.IsExtended(), - Length: uint8(def.Size), + Length: uint16(def.Size), SenderNode: string(def.Transmitter), } for _, signalDef := range def.Signals { @@ -73,8 +73,8 @@ func (c *compiler) collectDescriptors() { IsMultiplexer: signalDef.IsMultiplexerSwitch, IsMultiplexed: signalDef.IsMultiplexed, MultiplexerValue: uint(signalDef.MultiplexerSwitch), - Start: uint8(signalDef.StartBit), - Length: uint8(signalDef.Size), + Start: uint16(signalDef.StartBit), + Length: uint16(signalDef.Size), Scale: signalDef.Factor, Offset: signalDef.Offset, Min: signalDef.Minimum, diff --git a/payload.go b/payload.go new file mode 100644 index 0000000..2ecd881 --- /dev/null +++ b/payload.go @@ -0,0 +1,432 @@ +package can + +import ( + "encoding/hex" + "fmt" + "math/big" +) + +// const MaxDataLength = 8 + +// // Data holds the data in a CAN frame. +// // +// // Layout +// // +// // Individual bits in the data are numbered according to the following scheme: +// // +// // BIT +// // NUMBER +// // +------+------+------+------+------+------+------+------+ +// // | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// // BYTE +------+------+------+------+------+------+------+------+ +// // NUMBER +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 0 | | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 1 | | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 2 | | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 3 | | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 4 | | 39 | 38 | 37 | 36 | 35 | 34 | 33 | 32 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 5 | | 47 | 46 | 45 | 44 | 43 | 42 | 41 | 40 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 6 | | 55 | 54 | 53 | 52 | 51 | 50 | 49 | 48 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 7 | | 63 | 62 | 61 | 60 | 59 | 58 | 57 | 56 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // +// // Bit ranges can be manipulated using little-endian and big-endian bit ordering. +// // +// // Little-endian bit ranges +// // +// // Example range of length 32 starting at bit 29: +// // +// // BIT +// // NUMBER +// // +------+------+------+------+------+------+------+------+ +// // | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// // BYTE +------+------+------+------+------+------+------+------+ +// // NUMBER +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 0 | | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 1 | | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 2 | | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 3 | | <-------------LSb | 28 | 27 | 26 | 25 | 24 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 4 | | <-------------------------------------------------- | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 5 | | <-------------------------------------------------- | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 6 | | <-------------------------------------------------- | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 7 | | 63 | 62 | 61 | <-MSb--------------------------- | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // +// // Big-endian bit ranges +// // +// // Example range of length 32 starting at bit 29: +// // +// // BIT +// // NUMBER +// // +------+------+------+------+------+------+------+------+ +// // | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// // BYTE +------+------+------+------+------+------+------+------+ +// // NUMBER +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 0 | | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 1 | | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 2 | | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 3 | | 31 | 30 | <-MSb--------------------------------- | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 4 | | <-------------------------------------------------- | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 5 | | <-------------------------------------------------- | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 6 | | <-------------------------------------------------- | +// // +-----+ +------+------+------+------+------+------+------+------+ +// // | 7 | | <------LSb | 61 | 60 | 59 | 58 | 57 | 56 | +// // +-----+ +------+------+------+------+------+------+------+------+ +// type Data [MaxDataLength]byte + +//type Packed *big.Int + +type Payload struct { + // Message length + Length uint16 + + // Binary data + Data []byte + + // Packed little endian + PackedLittleEndian *big.Int + + // Packed big endian + PackedBigEndian *big.Int +} + +func (p *Payload) Hex() string { + h := hex.EncodeToString(p.Data) + return h +} + +func PayloadFromHex(hexString string) (Payload, error) { + b, err := hex.DecodeString(hexString) + var p Payload + if err != nil { + return p, err + } + p = Payload{Data: b} + return p, nil +} + +// UnsignedBitsLittleEndian returns the little-endian bit range [start, start+length) as an unsigned value. +func (p *Payload) UnsignedBitsLittleEndian(start, length uint16) uint64 { + // pack bits into one continuous value + + packed := p.PackLittleEndian() + // lsb index in the packed value is the start bit + lsbIndex := uint(start) + // shift away lower bits + shifted := packed.Rsh(packed, lsbIndex) + // mask away higher bits + //masked := shifted & ((1 << length) - 1) + masked := shifted.And(shifted, big.NewInt((1< 8*p.Length-1 { + return false + } + // calculate which byte the bit belongs to + byteIndex := i / 8 + // calculate bit mask for extracting the bit + bitMask := uint8(1 << (i % 8)) + // mocks the bit + bit := p.Data[byteIndex]&bitMask > 0 + // done + return bit +} + +// SetBit sets the value of the i:th bit in the data. +func (p *Payload) SetBit(i uint16, value bool) { + if i > 8*p.Length-1 { + return + } + byteIndex := i / 8 + bitIndex := i % 8 + if value { + p.Data[byteIndex] |= uint8(1 << bitIndex) + } else { + p.Data[byteIndex] &= ^uint8(1 << bitIndex) + } +} + +// PackLittleEndian packs the data into a contiguous uint64 value for little-endian signals. +// func (d *Data) PackLittleEndian() uint64 { +// var packed uint64 +// packed |= uint64(d[0]) << (0 * 8) +// packed |= uint64(d[1]) << (1 * 8) +// packed |= uint64(d[2]) << (2 * 8) +// packed |= uint64(d[3]) << (3 * 8) +// packed |= uint64(d[4]) << (4 * 8) +// packed |= uint64(d[5]) << (5 * 8) +// packed |= uint64(d[6]) << (6 * 8) +// packed |= uint64(d[7]) << (7 * 8) +// return packed +// } + +func (p *Payload) PackLittleEndian() *big.Int { + if p.PackedLittleEndian == nil { + // Initialize a big.Int called packed + //var packed, _ = new(big.Int).SetString(strings.Repeat("00000000", int(p.Length)), 2) + packed, _ := new(big.Int).SetString(CanBitsLittleEndian(p.Data), 2) + + // for i := 0; i < int(p.Length); i++ { + // //packed |= uint8(packed >> (i * 8)) + // fmt.Println(fmt.Sprintf("%08b", p.Data[i])) + // } + //packed.SetBytes(p.Data) + p.PackedLittleEndian = packed + } + return new(big.Int).Set(p.PackedLittleEndian) +} + +// CanBitsLittleEndian creates the zig zag pattern of bits feeding into a can.Data frame +func CanBitsLittleEndian(bytes []byte) string { + var bits string + for _, n := range bytes { + bn := fmt.Sprintf("%08b", n) + + // Need to reverse the binary strings because of the definition of bit order + bits += reverse(bn) + } + return reverse(bits) +} + +// CanBitsBigEndian creates the zig zag pattern of bits feeding into a can.Data frame +func CanBitsBigEndian(bytes []byte) string { + var bits string + for _, n := range bytes { + bn := fmt.Sprintf("%08b", n) + bits += bn + } + return bits +} + +// function to reverse strings +func reverse(s string) string { + runes := []rune(s) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] + } + return string(runes) +} + +// // PackBigEndian packs the data into a contiguous uint64 value for big-endian signals. +// func (d *Data) PackBigEndian() uint64 { +// var packed uint64 +// packed |= uint64(d[0]) << (7 * 8) +// packed |= uint64(d[1]) << (6 * 8) +// packed |= uint64(d[2]) << (5 * 8) +// packed |= uint64(d[3]) << (4 * 8) +// packed |= uint64(d[4]) << (3 * 8) +// packed |= uint64(d[5]) << (2 * 8) +// packed |= uint64(d[6]) << (1 * 8) +// packed |= uint64(d[7]) << (0 * 8) +// return packed +// } + +func (p *Payload) PackBigEndian() *big.Int { + if p.PackedBigEndian == nil { + var packed, _ = new(big.Int).SetString(CanBitsBigEndian(p.Data), 2) + p.PackedBigEndian = packed + } + return new(big.Int).Set(p.PackedBigEndian) +} + +// // UnpackLittleEndian sets the value of d.Bytes by unpacking the provided value as sequential little-endian bits. +// func (d *Data) UnpackLittleEndian(packed uint64) { +// d[0] = uint8(packed >> (0 * 8)) +// d[1] = uint8(packed >> (1 * 8)) +// d[2] = uint8(packed >> (2 * 8)) +// d[3] = uint8(packed >> (3 * 8)) +// d[4] = uint8(packed >> (4 * 8)) +// d[5] = uint8(packed >> (5 * 8)) +// d[6] = uint8(packed >> (6 * 8)) +// d[7] = uint8(packed >> (7 * 8)) +// } + +// // UnpackBigEndian sets the value of d.Bytes by unpacking the provided value as sequential big-endian bits. +// func (d *Data) UnpackBigEndian(packed uint64) { +// d[0] = uint8(packed >> (7 * 8)) +// d[1] = uint8(packed >> (6 * 8)) +// d[2] = uint8(packed >> (5 * 8)) +// d[3] = uint8(packed >> (4 * 8)) +// d[4] = uint8(packed >> (3 * 8)) +// d[5] = uint8(packed >> (2 * 8)) +// d[6] = uint8(packed >> (1 * 8)) +// d[7] = uint8(packed >> (0 * 8)) +// } + +// invertEndian converts from big-endian to little-endian bit indexing and vice versa. +func (p *Payload) invertEndian(i uint16) uint16 { + row := i / 8 + col := i % 8 + oppositeRow := uint16(len(p.Data)) - row - 1 + bitIndex := (oppositeRow * 8) + col + return bitIndex +} + +// // CheckBitRangeLittleEndian checks that a little-endian bit range fits in the data. +// func CheckBitRangeLittleEndian(frameLength, rangeStart, rangeLength uint8) error { +// lsbIndex := rangeStart +// msbIndex := rangeStart + rangeLength - 1 +// upperBound := frameLength * 8 +// if msbIndex >= upperBound { +// return fmt.Errorf("bit range out of bounds [0, %v): [%v, %v]", upperBound, lsbIndex, msbIndex) +// } +// return nil +// } + +// // CheckBitRangeBigEndian checks that a big-endian bit range fits in the data. +// func CheckBitRangeBigEndian(frameLength, rangeStart, rangeLength uint8) error { +// upperBound := frameLength * 8 +// if rangeStart >= upperBound { +// return fmt.Errorf("bit range starts out of bounds [0, %v): %v", upperBound, rangeStart) +// } +// msbIndex := invertEndian(rangeStart) +// lsbIndex := msbIndex - rangeLength + 1 +// end := invertEndian(lsbIndex) +// if end >= upperBound { +// return fmt.Errorf("bit range ends out of bounds [0, %v): %v", upperBound, end) +// } +// return nil +// } + +// // CheckValue checks that a value fits in a number of bits. +// func CheckValue(value uint64, bits uint8) error { +// upperBound := uint64(1 << bits) +// if value >= upperBound { +// return fmt.Errorf("value out of bounds [0, %v): %v", upperBound, value) +// } +// return nil +// } + +// AsSigned reinterprets the provided unsigned value as a signed value. +func AsSigned(unsigned uint64, bits uint16) int64 { + switch bits { + case 8: + return int64(int8(uint8(unsigned))) + case 16: + return int64(int16(uint16(unsigned))) + case 32: + return int64(int32(uint32(unsigned))) + case 64: + return int64(unsigned) + default: + // calculate bit mask for sign bit + signBitMask := uint64(1 << (bits - 1)) + // check if sign bit is set + isNegative := unsigned&signBitMask > 0 + if !isNegative { + // sign bit not set means we can reinterpret the value as-is + return int64(unsigned) + } + // calculate bit mask for extracting value bits (all bits except the sign bit) + valueBitMask := signBitMask - 1 + // calculate two's complement of the value bits + value := ((^unsigned) & valueBitMask) + 1 + // result is the negative value of the two's complement + return -1 * int64(value) + } +} diff --git a/payload_test.go b/payload_test.go new file mode 100644 index 0000000..0ea7e29 --- /dev/null +++ b/payload_test.go @@ -0,0 +1,108 @@ +package can + +import ( + "fmt" + "testing" +) + +type signals struct { + start uint16 + length uint16 + unsigned uint64 + signed int64 +} + +func TestPackLittleEndian(t *testing.T) { + // 302064448 + // 10010000000010010001101000000 + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + + dataLittleEndian := payload.PackLittleEndian() + fmt.Println(dataLittleEndian) + fmt.Println(fmt.Sprintf("%b", dataLittleEndian)) + + // if fmt.Sprintf("%08b", dataLittleEndian) != "10010000000010010001101000000" { + // t.Errorf("fmt.Sprintf('%08b', dataLittleEndian) = %s); want 10010000000010010001101000000", fmt.Sprintf("%08b", dataLittleEndian)) + // } +} + +func TestPackBigEndian(t *testing.T) { + // 4621538819433299968 + // 100000000100011000000010001001000000000000000000000000000000000 + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + + dataBigEndian := payload.PackBigEndian() + fmt.Println(dataBigEndian) + fmt.Println(fmt.Sprintf("%b", dataBigEndian)) + + // if fmt.Sprintf("%08b", dataBigEndian) != "10000000010001100000001000100100" { + // t.Errorf("fmt.Sprintf('%08b', dataBigEndian) = %s); want 10000000010001100000001000100100", fmt.Sprintf("%08b", dataBigEndian)) + // } +} + +func TestUnsignedLittleEndian(t *testing.T) { + // 18 + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + signal := signals{start: 24, length: 8, unsigned: 0x12, signed: 18} + fmt.Println(payload.UnsignedBitsLittleEndian(signal.start, signal.length)) +} + +func TestUnsignedBigEndian(t *testing.T) { + // 3219 + data := []byte{0x3f, 0xf7, 0x0d, 0xc4, 0x0c, 0x93, 0xff, 0xff} + payload := Payload{Data: data} + signal := signals{start: 39, length: 16, unsigned: 0xc93, signed: 3219} + fmt.Println(payload.UnsignedBitsBigEndian(signal.start, signal.length)) + +} + +func TestSignedLittleEndian(t *testing.T) { + // -1 + data := []byte{0x80, 0x01} + payload := Payload{Data: data} + signal := signals{start: 7, length: 2, unsigned: 0x3, signed: -1} + fmt.Println(payload.SignedBitsLittleEndian(signal.start, signal.length)) +} + +func TestSignedBigEndian(t *testing.T) { + // -9 + data := []byte{0x3f, 0xf7, 0x0d, 0xc4, 0x0c, 0x93, 0xff, 0xff} + payload := Payload{Data: data} + signal := signals{start: 3, length: 12, unsigned: 0xff7, signed: -9} + fmt.Println(payload.SignedBitsBigEndian(signal.start, signal.length)) +} + +func Benchmark4BytesPayload_PackLittleEndian(b *testing.B) { + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + for i := 0; i < b.N; i++ { + _ = payload.PackLittleEndian() + } +} + +func Benchmark4BytesPayload_PackBigEndian(b *testing.B) { + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + for i := 0; i < b.N; i++ { + _ = payload.PackBigEndian() + } +} + +func Benchmark4BytesPayload_UnsignedBitsLittleEndian(b *testing.B) { + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + for i := 0; i < b.N; i++ { + _ = payload.UnsignedBitsLittleEndian(0, 16) + } +} + +func Benchmark4BytesPayload_UnsignedBitsBigEndian(b *testing.B) { + data := []byte{0x40, 0x23, 0x01, 0x12} + payload := Payload{Data: data} + for i := 0; i < b.N; i++ { + _ = payload.UnsignedBitsBigEndian(0, 16) + } +} diff --git a/pkg/cantext/encode_test.go b/pkg/cantext/encode_test.go index bd2325b..ad50394 100644 --- a/pkg/cantext/encode_test.go +++ b/pkg/cantext/encode_test.go @@ -99,7 +99,7 @@ func newDriverHeartbeatDescriptor() *descriptor.Message { Name: (string)("DriverHeartbeat"), ID: (uint32)(100), IsExtended: (bool)(false), - Length: (uint8)(1), + Length: (uint16)(1), Description: (string)("Sync message used to synchronize the controllers"), SendType: descriptor.SendTypeCyclic, CycleTime: 100 * time.Millisecond, @@ -107,8 +107,8 @@ func newDriverHeartbeatDescriptor() *descriptor.Message { Signals: []*descriptor.Signal{ { Name: (string)("Command"), - Start: (uint8)(0), - Length: (uint8)(8), + Start: (uint16)(0), + Length: (uint16)(8), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -150,13 +150,13 @@ func newMotorStatusDescriptor() *descriptor.Message { Name: (string)("MotorStatus"), ID: (uint32)(400), IsExtended: (bool)(false), - Length: (uint8)(3), + Length: (uint16)(3), Description: (string)(""), Signals: []*descriptor.Signal{ { Name: (string)("WheelError"), - Start: (uint8)(0), - Length: (uint8)(1), + Start: (uint16)(0), + Length: (uint16)(1), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -177,8 +177,8 @@ func newMotorStatusDescriptor() *descriptor.Message { }, { Name: (string)("SpeedKph"), - Start: (uint8)(8), - Length: (uint8)(16), + Start: (uint16)(8), + Length: (uint16)(16), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), diff --git a/pkg/descriptor/message.go b/pkg/descriptor/message.go index 9f6a701..118bcd9 100644 --- a/pkg/descriptor/message.go +++ b/pkg/descriptor/message.go @@ -11,7 +11,7 @@ type Message struct { // IsExtended is true if the message is an extended CAN message. IsExtended bool // Length in bytes. - Length uint8 + Length uint16 // SendType is the message's send type. SendType SendType // Description of the message. diff --git a/pkg/descriptor/signal.go b/pkg/descriptor/signal.go index 4073cdb..7fc26d5 100644 --- a/pkg/descriptor/signal.go +++ b/pkg/descriptor/signal.go @@ -11,9 +11,9 @@ type Signal struct { // Description of the signal. Name string // Start bit. - Start uint8 + Start uint16 // Length in bits. - Length uint8 + Length uint16 // IsBigEndian is true if the signal is big-endian. IsBigEndian bool // IsSigned is true if the signal uses raw signed values. @@ -85,25 +85,52 @@ func (s *Signal) FromPhysical(physical float64) float64 { // UnmarshalPhysical returns the physical value of the signal in the provided CAN frame. func (s *Signal) UnmarshalPhysical(d can.Data) float64 { switch { - case s.Length == 1: - if d.Bit(s.Start) { + case uint8(s.Length) == 1: + if d.Bit(uint8(s.Start)) { return 1 } return 0 case s.IsSigned: var value int64 if s.IsBigEndian { - value = d.SignedBitsBigEndian(s.Start, s.Length) + value = d.SignedBitsBigEndian(uint8(s.Start), uint8(s.Length)) } else { - value = d.SignedBitsLittleEndian(s.Start, s.Length) + value = d.SignedBitsLittleEndian(uint8(s.Start), uint8(s.Length)) } return s.ToPhysical(float64(value)) default: var value uint64 if s.IsBigEndian { - value = d.UnsignedBitsBigEndian(s.Start, s.Length) + value = d.UnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length)) } else { - value = d.UnsignedBitsLittleEndian(s.Start, s.Length) + value = d.UnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length)) + } + return s.ToPhysical(float64(value)) + } +} + +// UnmarshalPhysicalPayload returns the physical value of the signal in the provided CAN frame. +func (s *Signal) UnmarshalPhysicalPayload(p *can.Payload) float64 { + switch { + case uint8(s.Length) == 1: + if p.Bit(s.Start) { + return 1 + } + return 0 + case s.IsSigned: + var value int64 + if s.IsBigEndian { + value = p.SignedBitsBigEndian(s.Start, s.Length) + } else { + value = p.SignedBitsLittleEndian(s.Start, s.Length) + } + return s.ToPhysical(float64(value)) + default: + var value uint64 + if s.IsBigEndian { + value = p.UnsignedBitsBigEndian(s.Start, s.Length) + } else { + value = p.UnsignedBitsLittleEndian(s.Start, s.Length) } return s.ToPhysical(float64(value)) } @@ -112,9 +139,17 @@ func (s *Signal) UnmarshalPhysical(d can.Data) float64 { // UnmarshalUnsigned returns the unsigned value of the signal in the provided CAN frame. func (s *Signal) UnmarshalUnsigned(d can.Data) uint64 { if s.IsBigEndian { - return d.UnsignedBitsBigEndian(s.Start, s.Length) + return d.UnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length)) } - return d.UnsignedBitsLittleEndian(s.Start, s.Length) + return d.UnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length)) +} + +// UnmarshalUnsignedPayload returns the unsigned value of the signal in the provided CAN frame. +func (s *Signal) UnmarshalUnsignedPayload(p *can.Payload) uint64 { + if s.IsBigEndian { + return p.UnsignedBitsBigEndian(s.Start, s.Length) + } + return p.UnsignedBitsLittleEndian(s.Start, s.Length) } // UnmarshalValueDescription returns the value description of the signal in the provided CAN data. @@ -131,55 +166,77 @@ func (s *Signal) UnmarshalValueDescription(d can.Data) (string, bool) { return s.ValueDescription(intValue) } +// UnmarshalValueDescriptionPayload returns the value description of the signal in the provided CAN data. +func (s *Signal) UnmarshalValueDescriptionPayload(p *can.Payload) (string, bool) { + if len(s.ValueDescriptions) == 0 { + return "", false + } + var intValue int + if s.IsSigned { + intValue = int(s.UnmarshalSignedPayload(p)) + } else { + intValue = int(s.UnmarshalUnsignedPayload(p)) + } + return s.ValueDescription(intValue) +} + // UnmarshalSigned returns the signed value of the signal in the provided CAN frame. func (s *Signal) UnmarshalSigned(d can.Data) int64 { if s.IsBigEndian { - return d.SignedBitsBigEndian(s.Start, s.Length) + return d.SignedBitsBigEndian(uint8(s.Start), uint8(uint8(s.Length))) + } + return d.SignedBitsLittleEndian(uint8(s.Start), uint8(uint8(s.Length))) +} + +// UnmarshalSignedPayload returns the signed value of the signal in the provided CAN frame. +func (s *Signal) UnmarshalSignedPayload(p *can.Payload) int64 { + if s.IsBigEndian { + return p.SignedBitsBigEndian(s.Start, s.Length) } - return d.SignedBitsLittleEndian(s.Start, s.Length) + return p.SignedBitsLittleEndian(s.Start, s.Length) } // UnmarshalBool returns the bool value of the signal in the provided CAN frame. func (s *Signal) UnmarshalBool(d can.Data) bool { - return d.Bit(s.Start) + return d.Bit(uint8(s.Start)) } // MarshalUnsigned sets the unsigned value of the signal in the provided CAN frame. func (s *Signal) MarshalUnsigned(d *can.Data, value uint64) { if s.IsBigEndian { - d.SetUnsignedBitsBigEndian(s.Start, s.Length, value) + d.SetUnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) } else { - d.SetUnsignedBitsLittleEndian(s.Start, s.Length, value) + d.SetUnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length), value) } } // MarshalSigned sets the signed value of the signal in the provided CAN frame. func (s *Signal) MarshalSigned(d *can.Data, value int64) { if s.IsBigEndian { - d.SetSignedBitsBigEndian(s.Start, s.Length, value) + d.SetSignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) } else { - d.SetSignedBitsLittleEndian(s.Start, s.Length, value) + d.SetSignedBitsLittleEndian(uint8(s.Start), uint8(s.Length), value) } } // MarshalBool sets the bool value of the signal in the provided CAN frame. func (s *Signal) MarshalBool(d *can.Data, value bool) { - d.SetBit(s.Start, value) + d.SetBit(uint8(s.Start), value) } // MaxUnsigned returns the maximum unsigned value representable by the signal. func (s *Signal) MaxUnsigned() uint64 { - return (2 << (s.Length - 1)) - 1 + return (2 << (uint8(s.Length) - 1)) - 1 } // MinSigned returns the minimum signed value representable by the signal. func (s *Signal) MinSigned() int64 { - return (2 << (s.Length - 1) / 2) * -1 + return (2 << (uint8(s.Length) - 1) / 2) * -1 } // MaxSigned returns the maximum signed value representable by the signal. func (s *Signal) MaxSigned() int64 { - return (2 << (s.Length - 1) / 2) - 1 + return (2 << (uint8(s.Length) - 1) / 2) - 1 } // SaturatedCastSigned performs a saturated cast of an int64 to the value domain of the signal. diff --git a/pkg/descriptor/signal_test.go b/pkg/descriptor/signal_test.go index 6102841..5479d39 100644 --- a/pkg/descriptor/signal_test.go +++ b/pkg/descriptor/signal_test.go @@ -49,7 +49,7 @@ func TestSignal_UnmarshalSigned_BigEndian(t *testing.T) { } const value int64 = -8 var data can.Data - data.SetSignedBitsBigEndian(s.Start, s.Length, value) + data.SetSignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) assert.Equal(t, value, s.UnmarshalSigned(data)) } @@ -62,7 +62,7 @@ func TestSignal_MarshalUnsigned_BigEndian(t *testing.T) { } const value uint64 = 8 var expected can.Data - expected.SetUnsignedBitsBigEndian(s.Start, s.Length, value) + expected.SetUnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) var actual can.Data s.MarshalUnsigned(&actual, value) assert.DeepEqual(t, expected, actual) @@ -78,7 +78,7 @@ func TestSignal_MarshalSigned_BigEndian(t *testing.T) { } const value int64 = -8 var expected can.Data - expected.SetSignedBitsBigEndian(s.Start, s.Length, value) + expected.SetSignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value) var actual can.Data s.MarshalSigned(&actual, value) assert.DeepEqual(t, expected, actual) diff --git a/pkg/socketcan/frame.go b/pkg/socketcan/frame.go index b565256..dddbaee 100644 --- a/pkg/socketcan/frame.go +++ b/pkg/socketcan/frame.go @@ -76,7 +76,7 @@ type frame struct { // idAndFlags is the combined CAN ID and flags. idAndFlags uint32 // dataLengthCode is the frame payload length in bytes. - dataLengthCode uint8 + dataLengthCode uint16 // padding+reserved fields _ [3]byte // bytes contains the frame payload. @@ -86,14 +86,14 @@ type frame struct { func (f *frame) unmarshalBinary(b []byte) { _ = b[lengthOfFrame-1] // bounds check f.idAndFlags = binary.LittleEndian.Uint32(b[indexOfID : indexOfID+lengthOfID]) - f.dataLengthCode = b[indexOfDataLengthCode] + f.dataLengthCode = uint16(b[indexOfDataLengthCode]) copy(f.data[:], b[indexOfData:lengthOfFrame]) } func (f *frame) marshalBinary(b []byte) { _ = b[lengthOfFrame-1] // bounds check binary.LittleEndian.PutUint32(b[indexOfID:indexOfID+lengthOfID], f.idAndFlags) - b[indexOfDataLengthCode] = f.dataLengthCode + b[indexOfDataLengthCode] = uint8(f.dataLengthCode) copy(b[indexOfData:], f.data[:]) } diff --git a/testdata/gen/go/example/example.dbc.go b/testdata/gen/go/example/example.dbc.go index c9d3773..0432930 100644 --- a/testdata/gen/go/example/example.dbc.go +++ b/testdata/gen/go/example/example.dbc.go @@ -2449,7 +2449,7 @@ var d = (*descriptor.Database)(&descriptor.Database{ Name: (string)("EmptyMessage"), ID: (uint32)(1), IsExtended: (bool)(false), - Length: (uint8)(0), + Length: (uint16)(0), SendType: (descriptor.SendType)(0), Description: (string)(""), Signals: ([]*descriptor.Signal)(nil), @@ -2461,14 +2461,14 @@ var d = (*descriptor.Database)(&descriptor.Database{ Name: (string)("DriverHeartbeat"), ID: (uint32)(100), IsExtended: (bool)(false), - Length: (uint8)(1), + Length: (uint16)(1), SendType: (descriptor.SendType)(1), Description: (string)("Sync message used to synchronize the controllers"), Signals: ([]*descriptor.Signal)([]*descriptor.Signal{ (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("Command"), - Start: (uint8)(0), - Length: (uint8)(8), + Start: (uint16)(0), + Length: (uint16)(8), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2509,14 +2509,14 @@ var d = (*descriptor.Database)(&descriptor.Database{ Name: (string)("MotorCommand"), ID: (uint32)(101), IsExtended: (bool)(false), - Length: (uint8)(1), + Length: (uint16)(1), SendType: (descriptor.SendType)(1), Description: (string)(""), Signals: ([]*descriptor.Signal)([]*descriptor.Signal{ (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("Steer"), - Start: (uint8)(0), - Length: (uint8)(4), + Start: (uint16)(0), + Length: (uint16)(4), IsBigEndian: (bool)(false), IsSigned: (bool)(true), IsMultiplexer: (bool)(false), @@ -2536,8 +2536,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("Drive"), - Start: (uint8)(4), - Length: (uint8)(4), + Start: (uint16)(4), + Length: (uint16)(4), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2564,14 +2564,14 @@ var d = (*descriptor.Database)(&descriptor.Database{ Name: (string)("SensorSonars"), ID: (uint32)(200), IsExtended: (bool)(false), - Length: (uint8)(8), + Length: (uint16)(8), SendType: (descriptor.SendType)(1), Description: (string)(""), Signals: ([]*descriptor.Signal)([]*descriptor.Signal{ (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("Mux"), - Start: (uint8)(0), - Length: (uint8)(4), + Start: (uint16)(0), + Length: (uint16)(4), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(true), @@ -2592,8 +2592,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("ErrCount"), - Start: (uint8)(4), - Length: (uint8)(12), + Start: (uint16)(4), + Length: (uint16)(12), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2614,8 +2614,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("Left"), - Start: (uint8)(16), - Length: (uint8)(12), + Start: (uint16)(16), + Length: (uint16)(12), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2636,8 +2636,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("NoFiltLeft"), - Start: (uint8)(16), - Length: (uint8)(12), + Start: (uint16)(16), + Length: (uint16)(12), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2657,8 +2657,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("Middle"), - Start: (uint8)(28), - Length: (uint8)(12), + Start: (uint16)(28), + Length: (uint16)(12), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2679,8 +2679,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("NoFiltMiddle"), - Start: (uint8)(28), - Length: (uint8)(12), + Start: (uint16)(28), + Length: (uint16)(12), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2700,8 +2700,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("Right"), - Start: (uint8)(40), - Length: (uint8)(12), + Start: (uint16)(40), + Length: (uint16)(12), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2722,8 +2722,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("NoFiltRight"), - Start: (uint8)(40), - Length: (uint8)(12), + Start: (uint16)(40), + Length: (uint16)(12), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2743,8 +2743,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("Rear"), - Start: (uint8)(52), - Length: (uint8)(12), + Start: (uint16)(52), + Length: (uint16)(12), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2765,8 +2765,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("NoFiltRear"), - Start: (uint8)(52), - Length: (uint8)(12), + Start: (uint16)(52), + Length: (uint16)(12), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2793,14 +2793,14 @@ var d = (*descriptor.Database)(&descriptor.Database{ Name: (string)("MotorStatus"), ID: (uint32)(400), IsExtended: (bool)(false), - Length: (uint8)(3), + Length: (uint16)(3), SendType: (descriptor.SendType)(1), Description: (string)(""), Signals: ([]*descriptor.Signal)([]*descriptor.Signal{ (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("WheelError"), - Start: (uint8)(0), - Length: (uint8)(1), + Start: (uint16)(0), + Length: (uint16)(1), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2821,8 +2821,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("SpeedKph"), - Start: (uint8)(8), - Length: (uint8)(16), + Start: (uint16)(8), + Length: (uint16)(16), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2850,14 +2850,14 @@ var d = (*descriptor.Database)(&descriptor.Database{ Name: (string)("IODebug"), ID: (uint32)(500), IsExtended: (bool)(false), - Length: (uint8)(6), + Length: (uint16)(6), SendType: (descriptor.SendType)(2), Description: (string)(""), Signals: ([]*descriptor.Signal)([]*descriptor.Signal{ (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("TestUnsigned"), - Start: (uint8)(0), - Length: (uint8)(8), + Start: (uint16)(0), + Length: (uint16)(8), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2877,8 +2877,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("TestEnum"), - Start: (uint8)(8), - Length: (uint8)(6), + Start: (uint16)(8), + Length: (uint16)(6), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2907,8 +2907,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("TestSigned"), - Start: (uint8)(16), - Length: (uint8)(8), + Start: (uint16)(16), + Length: (uint16)(8), IsBigEndian: (bool)(false), IsSigned: (bool)(true), IsMultiplexer: (bool)(false), @@ -2928,8 +2928,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("TestFloat"), - Start: (uint8)(24), - Length: (uint8)(8), + Start: (uint16)(24), + Length: (uint16)(8), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2949,8 +2949,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("TestBoolEnum"), - Start: (uint8)(32), - Length: (uint8)(1), + Start: (uint16)(32), + Length: (uint16)(1), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), @@ -2979,8 +2979,8 @@ var d = (*descriptor.Database)(&descriptor.Database{ }), (*descriptor.Signal)(&descriptor.Signal{ Name: (string)("TestScaledEnum"), - Start: (uint8)(40), - Length: (uint8)(2), + Start: (uint16)(40), + Length: (uint16)(2), IsBigEndian: (bool)(false), IsSigned: (bool)(false), IsMultiplexer: (bool)(false), diff --git a/tests/decode_test.go b/tests/decode_test.go new file mode 100644 index 0000000..aa84af8 --- /dev/null +++ b/tests/decode_test.go @@ -0,0 +1,373 @@ +package decode_test + +import ( + "fmt" + "testing" + + "go.einride.tech/can" + "go.einride.tech/can/internal/generate" + "go.einride.tech/can/pkg/descriptor" +) + +var ( + db = getDatabase() + dbc = []byte(` +VERSION "" +NS_ : +BS_: +BU_: DBG DRIVER IO MOTOR SENSOR + +BO_ 1 EmptyMessage: 0 DBG +BO_ 100 DriverHeartbeat: 1 DRIVER + SG_ Command : 0|8@1+ (1,0) [0|0] "" SENSOR,MOTOR +BO_ 101 MotorCommand: 1 DRIVER + SG_ Steer : 0|4@1- (1,-5) [-5|5] "" MOTOR + SG_ Drive : 4|4@1+ (1,0) [0|9] "" MOTOR +BO_ 400 MotorStatus: 3 MOTOR + SG_ WheelError : 0|1@1+ (1,0) [0|0] "" DRIVER,IO + SG_ SpeedKph : 8|16@1+ (0.001,0) [0|0] "km/h" DRIVER,IO + +BO_ 200 SensorSonars: 8 SENSOR + SG_ Mux M : 0|4@1+ (1,0) [0|0] "" DRIVER,IO + SG_ ErrCount : 4|12@1+ (1,0) [0|0] "" DRIVER,IO + SG_ Left m0 : 16|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ Middle m0 : 28|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ Right m0 : 40|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ Rear m0 : 52|12@1+ (0.1,0) [0|0] "" DRIVER,IO + SG_ NoFiltLeft m1 : 16|12@1+ (0.1,0) [0|0] "" DBG + SG_ NoFiltMiddle m1 : 28|12@1+ (0.1,0) [0|0] "" DBG + SG_ NoFiltRight m1 : 40|12@1+ (0.1,0) [0|0] "" DBG + SG_ NoFiltRear m1 : 52|12@1+ (0.1,0) [0|0] "" DBG + +BO_ 500 IODebug: 6 IO + SG_ TestUnsigned : 0|8@1+ (1,0) [0|0] "" DBG + SG_ TestEnum : 8|6@1+ (1,0) [0|0] "" DBG + SG_ TestSigned : 16|8@1- (1,0) [0|0] "" DBG + SG_ TestFloat : 24|8@1+ (0.5,0) [0|0] "" DBG + SG_ TestBoolEnum : 32|1@1+ (1,0) [0|0] "" DBG + SG_ TestScaledEnum : 40|2@1+ (2,0) [0|6] "" DBG + +BO_ 1530 VDM_DisconnectState: 14 VDM + SG_ VDM_LockCountRR : 91|20@0+ (1,0) [0|1048575] "" TCM + SG_ VDM_DiscoStateRR : 95|4@0+ (1,0) [0|5] "" TCM + SG_ VDM_CurrentSenseRR : 79|16@0+ (1,0) [0|65535] "" TCM + SG_ VDM_DiscoStateRR_Target : 64|1@0+ (1,0) [0|1] "" TCM + SG_ VDM_TargetSpeedRR : 63|15@0+ (0.125,-2048) [-2048|2047.875] "rad/s" TCM + SG_ VDM_LockCountRL : 35|20@0+ (1,0) [0|1048575] "" TCM + SG_ VDM_DiscoStateRL : 39|4@0+ (1,0) [0|5] "" TCM + SG_ VDM_CurrentSenseRL : 23|16@0+ (1,0) [0|65535] "" TCM + SG_ VDM_DiscoStateRL_Target : 8|1@0+ (1,0) [0|1] "" TCM + SG_ VDM_TargetSpeedRL : 7|15@0+ (0.125,-2048) [-2048|2047.875] "rad/s" TCM + +BO_ 1927 TMM_EACVariables: 14 TMM + SG_ TMM_EACEvapAirTempIn : 96|8@0+ (0.4,-20) [-19.6|82.4] "degC" TCM + SG_ TMM_CompressorSpeedActual : 80|16@0+ (1,-1) [0|8600] "RPM" TCM + SG_ TMM_FBTargetCompressorSpeed : 64|16@0+ (1,-8600) [-8599|8600] "RPM" TCM + SG_ TMM_FFTargetCompressorSpeed : 48|16@0+ (1,-1) [0|8600] "RPM" TCM + SG_ TMM_FBTargetSuctionPressure : 32|16@0+ (0.05,0) [0.05|3276.8] "kPa" TCM + SG_ TMM_FFTargetSuctionPressure : 16|16@0+ (0.05,0) [0.05|3276.8] "kPa" TCM + SG_ TMM_ChillerCoolantOutTTgt : 8|8@0+ (0.4,-20) [-19.6|82.4] "degC" TCM + SG_ TMM_EvapAirOutTTgt : 0|8@0+ (0.4,-20) [-19.6|82.4] "degC" TCM + +EV_ BrakeEngaged: 0 [0|1] "" 0 10 DUMMY_NODE_VECTOR0 Vector__XXX; +EV_ Torque: 1 [0|30000] "mNm" 500 16 DUMMY_NODE_VECTOR0 Vector__XXX; +CM_ EV_ BrakeEngaged "Brake fully engaged"; +CM_ BU_ DRIVER "The driver controller driving the car"; +CM_ BU_ MOTOR "The motor controller of the car"; +CM_ BU_ SENSOR "The sensor controller of the car"; +CM_ BO_ 100 "Sync message used to synchronize the controllers"; + +BA_DEF_ "BusType" STRING ; +BA_DEF_ BO_ "GenMsgSendType" ENUM "None","Cyclic","OnEvent"; +BA_DEF_ BO_ "GenMsgCycleTime" INT 0 0; +BA_DEF_ SG_ "FieldType" STRING ; +BA_DEF_ SG_ "GenSigStartValue" INT 0 10000; +BA_DEF_DEF_ "BusType" "CAN"; +BA_DEF_DEF_ "FieldType" ""; +BA_DEF_DEF_ "GenMsgCycleTime" 0; +BA_DEF_DEF_ "GenSigStartValue" 0; + +BA_ "GenMsgSendType" BO_ 1 0; +BA_ "GenMsgSendType" BO_ 100 1; +BA_ "GenMsgCycleTime" BO_ 100 1000; +BA_ "GenMsgSendType" BO_ 101 1; +BA_ "GenMsgCycleTime" BO_ 101 100; +BA_ "GenMsgSendType" BO_ 200 1; +BA_ "GenMsgCycleTime" BO_ 200 100; +BA_ "GenMsgSendType" BO_ 400 1; +BA_ "GenMsgCycleTime" BO_ 400 100; +BA_ "GenMsgSendType" BO_ 500 2; +BA_ "FieldType" SG_ 100 Command "Command"; +BA_ "FieldType" SG_ 500 TestEnum "TestEnum"; +BA_ "GenSigStartValue" SG_ 500 TestEnum 2; + +VAL_ 100 Command 2 "Reboot" 1 "Sync" 0 "None" ; +VAL_ 500 TestEnum 2 "Two" 1 "One" ; +VAL_ 500 TestScaledEnum 3 "Six" 2 "Four" 1 "Two" 0 "Zero" ; +VAL_ 500 TestBoolEnum 1 "One" 0 "Zero" ; +VAL_ 1530 VDM_DiscoStateRR 0 "Undefined" 1 "Locked" 2 "Unlocked" 3 "Locking" 4 "Unlocking" 5 "Faulted" ; +VAL_ 1530 VDM_DiscoStateRL 0 "Undefined" 1 "Locked" 2 "Unlocked" 3 "Locking" 4 "Unlocking" 5 "Faulted" ; + +SIG_GROUP_ 1530 VDM_DisconnectState 1 : VDM_DiscoStateRL_Target VDM_DiscoStateRL VDM_CurrentSenseRL VDM_TargetSpeedRL VDM_LockCountRL VDM_DiscoStateRR_Target VDM_DiscoStateRR VDM_CurrentSenseRR VDM_TargetSpeedRR VDM_LockCountRR; +`) +) + +type signal struct { + name string + value float64 + description string + unit string +} + +func getDatabase() descriptor.Database { + c, _ := generate.Compile("test.dbc", dbc) + db := *c.Database + return db +} + +func TestDecodeEACVariables(t *testing.T) { + + c, err := generate.Compile("test.dbc", dbc) + if err != nil { + t.Errorf("err = %v; want nil", err) + } + + db := *c.Database + message, _ := db.Message(uint32(1927)) + + canDataHexString := "008232204e027600ca4b0007d296" + + payload, err := can.PayloadFromHex(canDataHexString) + // ci := packet.Metadata().CaptureInfo + if err != nil { + t.Errorf("err = %v; want nil", err) + } + + fmt.Println(payload.Hex()) + + expected := []signal{ + { + name: "TMM_EvapAirOutTTgt", + value: 6.0, + description: "", + unit: "degC", + }, + { + name: "TMM_ChillerCoolantOutTTgt", + value: -10.0, + description: "", + unit: "degC", + }, + { + name: "TMM_FFTargetSuctionPressure", + value: 206.75, + description: "", + unit: "kPa", + }, + { + name: "TMM_FBTargetSuctionPressure", + value: 15.75, + description: "", + unit: "kPa", + }, + { + name: "TMM_FFTargetCompressorSpeed", + value: 100, + description: "", + unit: "RPM", + }, + { + name: "TMM_FBTargetCompressorSpeed", + value: 1000, + description: "", + unit: "RPM", + }, + { + name: "TMM_CompressorSpeedActual", + value: 1000, + description: "", + unit: "RPM", + }, + { + name: "TMM_EACEvapAirTempIn", + value: 10.0, + description: "", + unit: "degC", + }, + } + + var expectedMap = make(map[string]signal) + for _, s := range expected { + expectedMap[s.name] = s + } + + for _, signal := range message.Signals { + value := signal.UnmarshalPhysicalPayload(&payload) + name := signal.Name + + if value != expectedMap[name].value { + t.Errorf("signal[%s] value = %f ; expected: %f", name, value, expectedMap[name].value) + } + } +} + +func TestDecodeDisconnectState(t *testing.T) { + byteStringHex := "8000000420061880000005200600" + + p, err := can.PayloadFromHex(byteStringHex) + if err != nil { + fmt.Println(err) + } + fmt.Println(p.Data) + + expected := []signal{ + { + + name: "VDM_TargetSpeedRL", + value: 0.0, + description: "", + unit: "rad/s", + }, + { + + name: "VDM_DiscoStateRL_Target", + value: 0, + description: "", + unit: "", + }, + { + + name: "VDM_CurrentSenseRL", + value: 4, + description: "", + unit: "", + }, + { + + name: "VDM_DiscoStateRL", + value: 2, + description: "Unlocked", + unit: "", + }, + { + + name: "VDM_LockCountRL", + value: 1560, + description: "", + unit: "", + }, + { + + name: "VDM_TargetSpeedRR", + value: 0, + description: "", + unit: "rad/s", + }, + { + + name: "VDM_DiscoStateRR_Target", + value: 0, + description: "", + unit: "", + }, + { + + name: "VDM_CurrentSenseRR", + value: 5, + description: "", + unit: "", + }, + { + + name: "VDM_DiscoStateRR", + value: 2, + description: "Unlocked", + unit: "", + }, + { + + name: "VDM_LockCountRR", + value: 1536, + description: "", + unit: "", + }, + } + + var expectedMap = make(map[string]signal) + for _, s := range expected { + expectedMap[s.name] = s + } + + message, _ := db.Message(uint32(1530)) + for _, signal := range message.Signals { + value := signal.UnmarshalPhysicalPayload(&p) + valueDesc, _ := signal.UnmarshalValueDescriptionPayload(&p) + name := signal.Name + + if value != expectedMap[name].value { + t.Errorf("signal[%s] value = %f ; expected: %f", name, value, expectedMap[name].value) + } + + if valueDesc != expectedMap[name].description { + t.Errorf("signal[%s] value = %s ; expected: %s", name, valueDesc, expectedMap[name].description) + } + } +} + +func TestDecodeSensorSonarsData(t *testing.T) { + + data := can.Data{0x01, 0x01, 0x01, 0x02, 0x01, 0x00} + payload := can.Payload{Data: data[:]} + + message, _ := db.Message(uint32(500)) + fmt.Println(message.Length) + + for _, signal := range message.Signals { + value := signal.UnmarshalPhysicalPayload(&payload) + valueDesc, _ := signal.UnmarshalValueDescriptionPayload(&payload) + + valueFromData := signal.UnmarshalPhysical(data) + descFromData, _ := signal.UnmarshalValueDescription(data) + name := signal.Name + + if value != valueFromData { + t.Errorf("signal[%s] value = %f ; expected: %f", name, value, valueFromData) + } + + if valueDesc != descFromData { + t.Errorf("signal[%s] value = %s ; expected: %s", name, valueDesc, descFromData) + } + } +} + +func BenchmarkDecodeData(b *testing.B) { + + message, _ := db.Message(uint32(500)) + decodeSignal := func() { + data := can.Data{0x01, 0x01, 0x01, 0x02, 0x01, 0x00} + for _, signal := range message.Signals { + _ = signal.UnmarshalPhysical(data) + _, _ = signal.UnmarshalValueDescription(data) + } + } + for i := 0; i < b.N; i++ { + decodeSignal() + } +} + +func BenchmarkDecodePayload(b *testing.B) { + + // {0x01, 0x01, 0x01, 0x02, 0x01, 0x00} + + message, _ := db.Message(uint32(500)) + decodeSignal := func() { + data := can.Payload{Data: []byte{0x01, 0x01, 0x01, 0x02, 0x01, 0x00}} + for _, signal := range message.Signals { + _ = signal.UnmarshalPhysicalPayload(&data) + _, _ = signal.UnmarshalValueDescriptionPayload(&data) + } + } + for i := 0; i < b.N; i++ { + decodeSignal() + } +}