diff --git a/binary.go b/binary.go index bdb556b..a5db0ff 100644 --- a/binary.go +++ b/binary.go @@ -138,8 +138,7 @@ func BigEndianBytesToUnsignedInt(bytes []byte) int { // The function returns the integer value of the bytes. func BigEndianBytesToSignedInt(bytes []byte) int { if len(bytes) == 1 { - bytes = append([]byte{byte(0)}, bytes...) - return int(int16(binary.BigEndian.Uint16(bytes))) + return int(int8(bytes[0])) } else if len(bytes) == 2 { return int(int16(binary.BigEndian.Uint16(bytes))) } else if len(bytes) == 4 { @@ -290,8 +289,7 @@ func LittleEndianBytesToUnsignedInt(bytes []byte) int { // The function returns the integer value of the bytes. func LittleEndianBytesToSignedInt(bytes []byte) int { if len(bytes) == 1 { - bytes = append(bytes, byte(0)) - return int(int16(binary.LittleEndian.Uint16(bytes))) + return int(int8(bytes[0])) } else if len(bytes) == 2 { return int(int16(binary.LittleEndian.Uint16(bytes))) } else if len(bytes) == 4 { diff --git a/binary_test.go b/binary_test.go index b49c5c0..0dd9742 100644 --- a/binary_test.go +++ b/binary_test.go @@ -367,7 +367,7 @@ func TestBigEndianBytesToSignedInt(t *testing.T) { }) t.Run("Test 255 1 Byte", func(t *testing.T) { input := []byte{255} - expected := 255 + expected := -1 v := BigEndianBytesToSignedInt(input) if !reflect.DeepEqual(v, expected) { t.Errorf("Expected %v, got %v", expected, v) @@ -818,7 +818,7 @@ func TestLittleEndianBytesToSignedInt(t *testing.T) { }) t.Run("Test 255 1 Byte", func(t *testing.T) { input := []byte{255} - expected := 255 + expected := -1 v := LittleEndianBytesToSignedInt(input) if !reflect.DeepEqual(v, expected) { t.Errorf("Expected %v, got %v", expected, v) diff --git a/message.go b/message.go new file mode 100644 index 0000000..5a9433d --- /dev/null +++ b/message.go @@ -0,0 +1,157 @@ +package mapache + +import "fmt" + +// A Message is a single data message from a vehicle, comprised of a list of Fields. +type Message []Field + +// Length returns the number of fields in the message. +func (m Message) Length() int { + return len(m) +} + +// Size returns the total number of bytes in the message. +func (m Message) Size() int { + size := 0 + for _, field := range m { + size += field.Size + } + return size +} + +// FillFromBytes fills the Fields of a Message with the provided byte array. +// It decodes the bytes into integer values and stores them in the Value of each Field. +// It returns an error if the data length does not match the size of the Message. +func (m Message) FillFromBytes(data []byte) error { + if len(data) != m.Size() { + return fmt.Errorf("invalid data length, expected %d bytes, got %d", m.Size(), len(data)) + } + counter := 0 + for i, field := range m { + field.Bytes = data[counter : counter+field.Size] + counter += field.Size + m[i] = field.Decode() + } + return nil +} + +// FillFromInts fills the Fields of a Message with the provided integers. +// It encodes the integers into bytes and stores them in the Bytes of each Field. +// It returns an error if the number of integers does not match the number of Fields in the Message. +func (m Message) FillFromInts(ints []int) error { + if len(ints) != m.Length() { + return fmt.Errorf("invalid ints length, expected %d, got %d", m.Length(), len(ints)) + } + for i, field := range m { + field.Value = ints[i] + field, err := field.Encode() + if err != nil { + return err + } + m[i] = field + } + return nil +} + +// ExportSignals returns a list of all Signals contained in each Field of the Message. +// Basically just calls ExportSignals on each Field and concatenates the results. +func (m Message) ExportSignals() []Signal { + signals := []Signal{} + for _, field := range m { + signals = append(signals, field.ExportSignals()...) + } + return signals +} + +// Field is a single field of a message. It will always be at least 1 byte in size. +// It is meant to be an intermediary structure, purely for encoding and decoding byte arrays. +// A single field may contain multiple signals (tpyically when it contains multiple errors as boolean flags). +type Field struct { + // Name of the field. Will be mapped to a signal name unless otherwise specified by ExportSignalFunc. + Name string + // Bytes, Size, Sign, and Endian are used to properly decode and encode the signal. + Bytes []byte + Size int + Sign SignMode + Endian Endian + // Value is the integer value of the field. + Value int + // ExportSignalFunc is the function that is used to export the field as an array of signals. + ExportSignalFunc ExportSignalFunc +} + +// ExportSignalFunc is a function that indicates how a field should be exported as an array of signals. +// Any required scaling will be applied here. If ExportSignalFunc is not set, the field will be directly +// exported as a single signal without scaling. +type ExportSignalFunc func(Field) []Signal + +// NewField creates a new Field object with the given name, size, sign, endian, and export function. +// If no export function is provided, DefaultSignalExportFunc will be used. +func NewField(name string, size int, sign SignMode, endian Endian, exportSignalFunc ExportSignalFunc) Field { + return Field{ + Name: name, + Size: size, + Sign: sign, + Endian: endian, + ExportSignalFunc: exportSignalFunc, + } +} + +// Decode takes a Field object, decodes the bytes into an integer value, and returns the decoded Field object. +func (f Field) Decode() Field { + if f.Sign == Signed && f.Endian == BigEndian { + f.Value = BigEndianBytesToSignedInt(f.Bytes) + } else if f.Sign == Signed && f.Endian == LittleEndian { + f.Value = LittleEndianBytesToSignedInt(f.Bytes) + } else if f.Sign == Unsigned && f.Endian == BigEndian { + f.Value = BigEndianBytesToUnsignedInt(f.Bytes) + } else if f.Sign == Unsigned && f.Endian == LittleEndian { + f.Value = LittleEndianBytesToUnsignedInt(f.Bytes) + } + return f +} + +// Encode takes a Field object, encodes the integer value into bytes, and returns the encoded Field object. +func (f Field) Encode() (Field, error) { + var err error + if f.Sign == Signed && f.Endian == BigEndian { + f.Bytes, err = BigEndianSignedIntToBinary(f.Value, f.Size) + } else if f.Sign == Signed && f.Endian == LittleEndian { + f.Bytes, err = LittleEndianSignedIntToBinary(f.Value, f.Size) + } else if f.Sign == Unsigned && f.Endian == BigEndian { + f.Bytes, err = BigEndianUnsignedIntToBinary(f.Value, f.Size) + } else if f.Sign == Unsigned && f.Endian == LittleEndian { + f.Bytes, err = LittleEndianUnsignedIntToBinary(f.Value, f.Size) + } else { + return f, fmt.Errorf("invalid sign or endian") + } + return f, err +} + +// CheckBit takes a Field object and a bit position, and returns the integer value of the bit at the given position (0 or 1). +// Bit positions are counted from left to right, where bit 0 is the leftmost bit. +func (f Field) CheckBit(bit int) int { + byteIndex := bit / 8 + bitPosition := 7 - (bit % 8) + if byteIndex >= len(f.Bytes) { + return 0 + } + return int((f.Bytes[byteIndex] >> bitPosition) & 1) +} + +// ExportSignals takes a Field object and exports it as an array of signals. +func (f Field) ExportSignals() []Signal { + if f.ExportSignalFunc == nil { + return DefaultSignalExportFunc(f) + } + return f.ExportSignalFunc(f) +} + +// DefaultSignalExportFunc is the default export function for a field. It exports the field as a single signal with no scaling. +func DefaultSignalExportFunc(f Field) []Signal { + return []Signal{Signal{ + Name: f.Name, + Value: float64(f.Value), + RawValue: f.Value, + }} +} diff --git a/message_test.go b/message_test.go new file mode 100644 index 0000000..e3a0a93 --- /dev/null +++ b/message_test.go @@ -0,0 +1,448 @@ +package mapache + +import ( + "testing" +) + +func TestMessage(t *testing.T) { + ecuStatusMessage := Message{ + NewField("ecu_state", 1, Unsigned, BigEndian, nil), + NewField("ecu_status_flags", 3, Unsigned, BigEndian, func(f Field) []Signal { + signals := []Signal{} + bitMap := []string{ + "ecu_status_acu", + "ecu_status_inv_one", + "ecu_status_inv_two", + "ecu_status_inv_three", + "ecu_status_inv_four", + "ecu_status_fan_one", + "ecu_status_fan_two", + "ecu_status_fan_three", + "ecu_status_fan_four", + "ecu_status_fan_five", + "ecu_status_fan_six", + "ecu_status_fan_seven", + "ecu_status_fan_eight", + "ecu_status_dash", + "ecu_status_steering", + } + for i := 0; i < len(bitMap); i++ { + signals = append(signals, Signal{ + Name: bitMap[i], + Value: float64(f.CheckBit(i)), + RawValue: f.CheckBit(i), + }) + } + return signals + }), + NewField("ecu_maps", 1, Unsigned, BigEndian, func(f Field) []Signal { + signals := []Signal{} + signals = append(signals, Signal{ + Name: "ecu_power_level", + Value: float64((f.Value >> 4) & 0x0F), + RawValue: (f.Value >> 4) & 0x0F, + }) + signals = append(signals, Signal{ + Name: "ecu_torque_map", + Value: float64(f.Value & 0x0F), + RawValue: f.Value & 0x0F, + }) + return signals + }), + NewField("ecu_max_cell_temp", 1, Unsigned, BigEndian, func(f Field) []Signal { + signals := []Signal{} + signals = append(signals, Signal{ + Name: "ecu_max_cell_temp", + Value: float64(f.Value) * 0.25, + RawValue: f.Value, + }) + return signals + }), + NewField("ecu_acu_state_of_charge", 1, Unsigned, BigEndian, func(f Field) []Signal { + signals := []Signal{} + signals = append(signals, Signal{ + Name: "ecu_acu_state_of_charge", + Value: float64(f.Value) * 20 / 51, + RawValue: f.Value, + }) + return signals + }), + NewField("ecu_glv_state_of_charge", 1, Unsigned, BigEndian, func(f Field) []Signal { + signals := []Signal{} + signals = append(signals, Signal{ + Name: "ecu_glv_state_of_charge", + Value: float64(f.Value) * 20 / 51, + RawValue: f.Value, + }) + return signals + }), + } + t.Run("Invalid byte length", func(t *testing.T) { + err := ecuStatusMessage.FillFromBytes([]byte{0, 0}) + if err == nil { + t.Errorf("Expected error, got nil") + } + }) + t.Run("Test zero values", func(t *testing.T) { + err := ecuStatusMessage.FillFromBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + if err != nil { + t.Errorf("Expected nil, got %v", err) + } + signals := ecuStatusMessage.ExportSignals() + for _, signal := range signals { + if signal.Value != 0 { + t.Errorf("Expected Value 0, got %f", signal.Value) + } + if signal.RawValue != 0 { + t.Errorf("Expected RawValue 0, got %d", signal.RawValue) + } + } + }) + t.Run("Test nonzero values", func(t *testing.T) { + err := ecuStatusMessage.FillFromBytes([]byte{0x12, 0x42, 0xFF, 0x00, 0x31, 0x82, 0x58, 0x72}) + if err != nil { + t.Errorf("Expected nil, got %v", err) + } + signals := ecuStatusMessage.ExportSignals() + expectedValues := []float64{ + 18, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 1, + 32.5, + 34.509804, + 44.705882, + } + for i, signal := range signals { + if int(signal.Value) != int(expectedValues[i]) { + t.Errorf("Expected Value %f, got %f", expectedValues[i], signal.Value) + } + } + }) +} + +func TestNewField(t *testing.T) { + field := NewField("test", 1, Unsigned, BigEndian, nil) + if field.Size != 1 { + t.Errorf("Expected Size 1, got %d", field.Size) + } + if field.Sign != Unsigned { + t.Errorf("Expected Sign Unsigned, got %d", field.Sign) + } +} + +func TestDecode(t *testing.T) { + testCases := []struct { + name string + field Field + expected int64 + }{ + { + name: "Signed BigEndian Positive", + field: Field{ + Bytes: []byte{0x12, 0x34}, + Size: 2, + Sign: Signed, + Endian: BigEndian, + }, + expected: 0x1234, + }, + { + name: "Signed BigEndian Negative", + field: Field{ + Bytes: []byte{0xFF, 0xFE}, + Size: 2, + Sign: Signed, + Endian: BigEndian, + }, + expected: -2, + }, + { + name: "Signed LittleEndian Positive", + field: Field{ + Bytes: []byte{0x34, 0x12}, + Size: 2, + Sign: Signed, + Endian: LittleEndian, + }, + expected: 0x1234, + }, + { + name: "Signed LittleEndian Negative", + field: Field{ + Bytes: []byte{0xFE, 0xFF}, + Size: 2, + Sign: Signed, + Endian: LittleEndian, + }, + expected: -2, + }, + { + name: "Unsigned BigEndian", + field: Field{ + Bytes: []byte{0xFF, 0xFE}, + Size: 2, + Sign: Unsigned, + Endian: BigEndian, + }, + expected: 0xFFFE, + }, + { + name: "Unsigned LittleEndian", + field: Field{ + Bytes: []byte{0xFE, 0xFF}, + Size: 2, + Sign: Unsigned, + Endian: LittleEndian, + }, + expected: 0xFFFE, + }, + { + name: "Single Byte Signed Positive", + field: Field{ + Bytes: []byte{0x7F}, + Size: 1, + Sign: Signed, + Endian: BigEndian, + }, + expected: 127, + }, + { + name: "Single Byte Signed Negative", + field: Field{ + Bytes: []byte{0xCF}, + Size: 1, + Sign: Signed, + Endian: BigEndian, + }, + expected: -49, + }, + { + name: "Four Bytes Unsigned BigEndian", + field: Field{ + Bytes: []byte{0x12, 0x34, 0x56, 0x78}, + Size: 4, + Sign: Unsigned, + Endian: BigEndian, + }, + expected: 0x12345678, + }, + { + name: "Four Bytes Unsigned LittleEndian", + field: Field{ + Bytes: []byte{0x78, 0x56, 0x34, 0x12}, + Size: 4, + Sign: Unsigned, + Endian: LittleEndian, + }, + expected: 0x12345678, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := tc.field.Decode() + if result.Value != int(tc.expected) { + t.Errorf("Expected %d (0x%X), got %d (0x%X)", + tc.expected, tc.expected, result.Value, result.Value) + } + }) + } +} + +func TestEncode(t *testing.T) { + testCases := []struct { + name string + field Field + expected []byte + expectError bool + }{ + { + name: "Signed BigEndian Positive", + field: Field{ + Value: 0x1234, + Size: 2, + Sign: Signed, + Endian: BigEndian, + }, + expected: []byte{0x12, 0x34}, + }, + { + name: "Signed BigEndian Negative", + field: Field{ + Value: -2, + Size: 2, + Sign: Signed, + Endian: BigEndian, + }, + expected: []byte{0xFF, 0xFE}, + }, + { + name: "Signed LittleEndian Positive", + field: Field{ + Value: 0x1234, + Size: 2, + Sign: Signed, + Endian: LittleEndian, + }, + expected: []byte{0x34, 0x12}, + }, + { + name: "Signed LittleEndian Negative", + field: Field{ + Value: -2, + Size: 2, + Sign: Signed, + Endian: LittleEndian, + }, + expected: []byte{0xFE, 0xFF}, + }, + { + name: "Unsigned BigEndian", + field: Field{ + Value: 0xFFFE, + Size: 2, + Sign: Unsigned, + Endian: BigEndian, + }, + expected: []byte{0xFF, 0xFE}, + }, + { + name: "Unsigned LittleEndian", + field: Field{ + Value: 0xFFFE, + Size: 2, + Sign: Unsigned, + Endian: LittleEndian, + }, + expected: []byte{0xFE, 0xFF}, + }, + { + name: "Single Byte Signed Positive", + field: Field{ + Value: 127, + Size: 1, + Sign: Signed, + Endian: BigEndian, + }, + expected: []byte{0x7F}, + }, + { + name: "Single Byte Signed Negative", + field: Field{ + Value: -49, + Size: 1, + Sign: Signed, + Endian: BigEndian, + }, + expected: []byte{0xCF}, + }, + { + name: "Four Bytes Unsigned BigEndian", + field: Field{ + Value: 0x12345678, + Size: 4, + Sign: Unsigned, + Endian: BigEndian, + }, + expected: []byte{0x12, 0x34, 0x56, 0x78}, + }, + { + name: "Four Bytes Unsigned LittleEndian", + field: Field{ + Value: 0x12345678, + Size: 4, + Sign: Unsigned, + Endian: LittleEndian, + }, + expected: []byte{0x78, 0x56, 0x34, 0x12}, + }, + { + name: "Value Too Large For Size", + field: Field{ + Value: 0x1234, + Size: 1, + Sign: Unsigned, + Endian: BigEndian, + }, + expectError: true, + }, + { + name: "Negative Value For Unsigned", + field: Field{ + Value: -1, + Size: 2, + Sign: Unsigned, + Endian: BigEndian, + }, + expectError: true, + }, + { + name: "Invalid Sign Value", + field: Field{ + Value: 123, + Size: 2, + Sign: 3, // Invalid sign value + Endian: BigEndian, + }, + expectError: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result, err := tc.field.Encode() + + if tc.expectError { + if err == nil { + t.Error("Expected error but got nil") + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + if len(result.Bytes) != len(tc.expected) { + t.Errorf("Expected %d bytes, got %d bytes", len(tc.expected), len(result.Bytes)) + return + } + + for i := 0; i < len(tc.expected); i++ { + if result.Bytes[i] != tc.expected[i] { + t.Errorf("Byte %d: expected 0x%02X, got 0x%02X", + i, tc.expected[i], result.Bytes[i]) + } + } + }) + } +} + +func TestCheckBit(t *testing.T) { + testBytes := []byte{0x12, 0x34} + field := Field{ + Bytes: testBytes, + Size: len(testBytes), + } + for i := 0; i < field.Size*8; i++ { + if field.CheckBit(i) != int((testBytes[i/8]>>uint(7-i%8))&1) { + t.Errorf("Expected %d, got %d", int((testBytes[i/8]>>uint(7-i%8))&1), field.CheckBit(i)) + } + } +} diff --git a/nodes.go b/nodes.go deleted file mode 100644 index 3d0a8f7..0000000 --- a/nodes.go +++ /dev/null @@ -1,55 +0,0 @@ -package mapache - -import "fmt" - -// Node is a type to represent a vehicle node. It is a slice of Signals, -// where each Signal represents a specific item in the vehicle node. -type Node []Signal - -// Length returns the number of fields in a Node. -func (n Node) Length() int { - return len(n) -} - -// Size returns the total size of a Node in bytes. -func (n Node) Size() int { - size := 0 - for _, field := range n { - size += field.Size - } - return size -} - -// FillFromBytes fills the fields of a Node with the bytes provided in the data slice. -// It decodes the bytes into integer values and stores them in the RawValue field of each Signal. -// It returns an error if the length of the data slice does not match the total size of the Node. -func (n Node) FillFromBytes(data []byte) error { - if len(data) != n.Size() { - return fmt.Errorf("invalid data length, expected %d bytes", n.Size()) - } - counter := 0 - for i, signal := range n { - signal.Bytes = data[counter : counter+signal.Size] - counter += signal.Size - n[i] = signal.Decode() - } - return nil -} - -// FillFromInts fills the fields of a Node with the integer values provided in the ints slice. -// It encodes the integer values into bytes and stores them in the Bytes field of each Signal. -// It returns an error if the length of the ints slice does not match the number of fields in the Node. -func (n Node) FillFromInts(ints []int) error { - if len(ints) != n.Length() { - return fmt.Errorf("invalid ints length, expected %d ints", n.Length()) - } - for i, signal := range n { - signal.RawValue = ints[i] - signal, err := signal.Encode() - if err != nil { - return err - } - n[i] = signal - } - return nil -} diff --git a/nodes_test.go b/nodes_test.go deleted file mode 100644 index 0ce1266..0000000 --- a/nodes_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package mapache - -// import ( -// "reflect" -// "testing" -// ) - -// func TestField_CheckBit(t *testing.T) { -// t.Run("Test Bit Set", func(t *testing.T) { -// f := Field{ -// Bytes: []byte{0x01}, -// } -// if !f.CheckBit(0) { -// t.Error("Expected true, got", f.CheckBit(0)) -// } -// }) -// t.Run("Test Bit Not Set", func(t *testing.T) { -// f := Field{ -// Bytes: []byte{0x00}, -// } -// if f.CheckBit(0) { -// t.Error("Expected false, got", f.CheckBit(0)) -// } -// }) -// } - -// func TestField_String(t *testing.T) { -// f := Field{ -// Name: "test", -// Value: 1, -// } -// if f.String() != "test: 1" { -// t.Error("Expected test: 1, got", f.String()) -// } -// } - -// func TestNewField(t *testing.T) { -// f := NewField("test", 1, Signed, BigEndian) -// if f.Name != "test" || f.Size != 1 || f.Sign != Signed || f.Endian != BigEndian { -// t.Error("Expected test, 1, Signed, BigEndian, got", f.Name, f.Size, f.Sign, f.Endian) -// } -// } - -// func TestNode_FillFromBytes(t *testing.T) { -// t.Run("Test FillFromBytes", func(t *testing.T) { -// n := Node{ -// {Name: "test", Size: 2, Sign: Unsigned, Endian: BigEndian}, -// } -// err := n.FillFromBytes([]byte{0x00, 0x01}) -// if err != nil { -// t.Error("Expected nil, got", err) -// } -// if n[0].Value != 1 { -// t.Error("Expected 1, got", n[0].Value) -// } -// }) -// t.Run("Test FillFromBytes Error", func(t *testing.T) { -// n := Node{ -// {Name: "test", Size: 2, Sign: Unsigned, Endian: BigEndian}, -// } -// err := n.FillFromBytes([]byte{0x00}) -// if err == nil { -// t.Error("Expected err, got nil") -// } -// }) -// } - -// func TestNode_FillFromInts(t *testing.T) { -// t.Run("Test FillFromInts", func(t *testing.T) { -// n := Node{ -// {Name: "test", Size: 2, Sign: Unsigned, Endian: BigEndian}, -// } -// err := n.FillFromInts([]int{1}) -// if err != nil { -// t.Error("Expected nil, got", err) -// } -// if n[0].Value != 1 { -// t.Error("Expected 1, got", n[0].Value) -// } -// }) -// t.Run("Test FillFromInts Error", func(t *testing.T) { -// n := Node{ -// {Name: "test", Size: 2, Sign: Unsigned, Endian: BigEndian}, -// } -// err := n.FillFromInts([]int{}) -// if err == nil { -// t.Error("Expected err, got nil") -// } -// }) -// t.Run("Test FillFromInts Error 2", func(t *testing.T) { -// n := Node{ -// {Name: "test", Size: 2, Sign: -1, Endian: -1}, -// } -// err := n.FillFromInts([]int{1}) -// if err == nil { -// t.Error("Expected err, got nil") -// } -// }) -// } - -// func TestNode_Length(t *testing.T) { -// n := Node{ -// {Name: "test", Size: 1}, -// } -// if n.Length() != 1 { -// t.Error("Expected 1, got", n.Length()) -// } -// } - -// func TestNode_Size(t *testing.T) { -// n := Node{ -// {Name: "test", Size: 4}, -// } -// if n.Size() != 4 { -// t.Error("Expected 4, got", n.Size()) -// } -// } - -// func TestNode_String(t *testing.T) { -// n := Node{ -// {Name: "test", Value: 1}, -// } -// if n.String() != "test: 1\n" { -// t.Error("Expected test: 1, got", n.String()) -// } -// } diff --git a/signal.go b/signal.go index 8fec35a..d0ff7f9 100644 --- a/signal.go +++ b/signal.go @@ -1,7 +1,6 @@ package mapache import ( - "fmt" "time" ) @@ -38,84 +37,8 @@ type Signal struct { ProducedAt time.Time `json:"produced_at"` // CreatedAt is the time at which the signal was actually stored in the database. CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;precision:6"` - - // Bytes, Size, Sign, and Endian are used to properly decode and encode the signal. - Bytes []byte `json:"-" gorm:"-"` - Size int `json:"-" gorm:"-"` - Sign SignMode `json:"-" gorm:"-"` - Endian Endian `json:"-" gorm:"-"` - // ScalingFunc is the function that is used to scale the signal. - ScalingFunc ScalingFunc `json:"-" gorm:"-"` } func (Signal) TableName() string { return "signal" } - -// ScalingFunc is a function that indicated how a value should be scaled. -type ScalingFunc func(float64) float64 - -// Scale scales the value of the signal by the given function. -func (s Signal) Scale() Signal { - if s.ScalingFunc == nil { - return s - } - s.Value = s.ScalingFunc(float64(s.RawValue)) - return s -} - -// Decode decodes the bytes stored in a Signal object and returns a signal object with the decoded raw value. -func (s Signal) Decode() Signal { - if s.Sign == Signed && s.Endian == BigEndian { - s.RawValue = BigEndianBytesToSignedInt(s.Bytes) - } else if s.Sign == Signed && s.Endian == LittleEndian { - s.RawValue = LittleEndianBytesToSignedInt(s.Bytes) - } else if s.Sign == Unsigned && s.Endian == BigEndian { - s.RawValue = BigEndianBytesToUnsignedInt(s.Bytes) - } else if s.Sign == Unsigned && s.Endian == LittleEndian { - s.RawValue = LittleEndianBytesToUnsignedInt(s.Bytes) - } - return s -} - -// Encode encodes the integer value stored in a Field object and returns a signal object with the encoded bytes. -func (s Signal) Encode() (Signal, error) { - var err error - if s.Sign == Signed && s.Endian == BigEndian { - s.Bytes, err = BigEndianSignedIntToBinary(s.RawValue, s.Size) - } else if s.Sign == Signed && s.Endian == LittleEndian { - s.Bytes, err = LittleEndianSignedIntToBinary(s.RawValue, s.Size) - } else if s.Sign == Unsigned && s.Endian == BigEndian { - s.Bytes, err = BigEndianUnsignedIntToBinary(s.RawValue, s.Size) - } else if s.Sign == Unsigned && s.Endian == LittleEndian { - s.Bytes, err = LittleEndianUnsignedIntToBinary(s.RawValue, s.Size) - } else { - return s, fmt.Errorf("invalid sign or endian") - } - return s, err -} - -// CheckBit returns a signal object with the raw value set to 1 if the bit at the given position is set, otherwise 0. -// Useful if the signal is a byte that contains multiple boolean flags. -func (s Signal) CheckBit(bit int) Signal { - v := (s.Bytes[0] & (1 << bit)) != 0 - if v { - s.RawValue = 1 - } else { - s.RawValue = 0 - } - return s -} - -// NewSignal creates a new Signal object with the provided vehicle ID, name, size, sign, endian, and scaling function. -// If the scaling function is nil, the signal will not be scaled (default to just x1). -func NewSignal(vehicleID string, name string, size int, sign SignMode, endian Endian, scalingFunc ScalingFunc) Signal { - return Signal{ - VehicleID: vehicleID, - Name: name, - Size: size, - Sign: sign, - Endian: endian, - ScalingFunc: scalingFunc, - } -} diff --git a/signal_test.go b/signal_test.go index 0214f1f..15d4172 100644 --- a/signal_test.go +++ b/signal_test.go @@ -1,138 +1,138 @@ package mapache -import ( - "reflect" - "testing" -) +// import ( +// "reflect" +// "testing" +// ) -func TestSignal_Decode(t *testing.T) { - t.Run("Test Big Endian Unsigned", func(t *testing.T) { - f := Signal{ - Bytes: []byte{0x00, 0x01}, - Sign: Unsigned, - Endian: BigEndian, - } - if f.Decode().RawValue != 1 { - t.Error("Expected 1, got", f.Decode().RawValue) - } - }) - t.Run("Test Big Endian Signed", func(t *testing.T) { - f := Signal{ - Bytes: []byte{0x00, 0x01}, - Sign: Signed, - Endian: BigEndian, - } - if f.Decode().RawValue != 1 { - t.Error("Expected 1, got", f.Decode().RawValue) - } - }) - t.Run("Test Little Endian Unsigned", func(t *testing.T) { - f := Signal{ - Bytes: []byte{0x00, 0x01}, - Sign: Unsigned, - Endian: LittleEndian, - } - if f.Decode().RawValue != 256 { - t.Error("Expected 256, got", f.Decode().RawValue) - } - }) - t.Run("Test Little Endian Signed", func(t *testing.T) { - f := Signal{ - Bytes: []byte{0x00, 0x01}, - Sign: Signed, - Endian: LittleEndian, - } - if f.Decode().RawValue != 256 { - t.Error("Expected 256, got", f.Decode().RawValue) - } - }) - t.Run("Test Other", func(t *testing.T) { - f := Signal{ - Bytes: []byte{0x00, 0x01}, - Sign: -1, - Endian: -1, - } - if f.Decode().RawValue != 0 { - t.Error("Expected 0, got", f.Decode().RawValue) - } - }) -} +// func TestSignal_Decode(t *testing.T) { +// t.Run("Test Big Endian Unsigned", func(t *testing.T) { +// f := Signal{ +// Bytes: []byte{0x00, 0x01}, +// Sign: Unsigned, +// Endian: BigEndian, +// } +// if f.Decode().RawValue != 1 { +// t.Error("Expected 1, got", f.Decode().RawValue) +// } +// }) +// t.Run("Test Big Endian Signed", func(t *testing.T) { +// f := Signal{ +// Bytes: []byte{0x00, 0x01}, +// Sign: Signed, +// Endian: BigEndian, +// } +// if f.Decode().RawValue != 1 { +// t.Error("Expected 1, got", f.Decode().RawValue) +// } +// }) +// t.Run("Test Little Endian Unsigned", func(t *testing.T) { +// f := Signal{ +// Bytes: []byte{0x00, 0x01}, +// Sign: Unsigned, +// Endian: LittleEndian, +// } +// if f.Decode().RawValue != 256 { +// t.Error("Expected 256, got", f.Decode().RawValue) +// } +// }) +// t.Run("Test Little Endian Signed", func(t *testing.T) { +// f := Signal{ +// Bytes: []byte{0x00, 0x01}, +// Sign: Signed, +// Endian: LittleEndian, +// } +// if f.Decode().RawValue != 256 { +// t.Error("Expected 256, got", f.Decode().RawValue) +// } +// }) +// t.Run("Test Other", func(t *testing.T) { +// f := Signal{ +// Bytes: []byte{0x00, 0x01}, +// Sign: -1, +// Endian: -1, +// } +// if f.Decode().RawValue != 0 { +// t.Error("Expected 0, got", f.Decode().RawValue) +// } +// }) +// } -func TestSignal_Encode(t *testing.T) { - t.Run("Test Big Endian Unsigned", func(t *testing.T) { - f := Signal{ - Sign: Unsigned, - Endian: BigEndian, - RawValue: 1, - Size: 2, - } - b, err := f.Encode() - if err != nil { - t.Error("Expected nil, got", err) - } - expected := []byte{0x00, 0x01} - if !reflect.DeepEqual(b.Bytes, expected) { - t.Error("Expected", expected, "got", b.Bytes) - } - }) - t.Run("Test Big Endian Signed", func(t *testing.T) { - f := Signal{ - Sign: Signed, - Endian: BigEndian, - RawValue: 1, - Size: 2, - } - b, err := f.Encode() - if err != nil { - t.Error("Expected nil, got", err) - } - expected := []byte{0x00, 0x01} - if !reflect.DeepEqual(b.Bytes, expected) { - t.Error("Expected", expected, "got", b.Bytes) - } - }) - t.Run("Test Little Endian Unsigned", func(t *testing.T) { - f := Signal{ - Sign: Unsigned, - Endian: LittleEndian, - RawValue: 1, - Size: 2, - } - b, err := f.Encode() - if err != nil { - t.Error("Expected nil, got", err) - } - expected := []byte{0x01, 0x00} - if !reflect.DeepEqual(b.Bytes, expected) { - t.Error("Expected", expected, "got", b.Bytes) - } - }) - t.Run("Test Little Endian Signed", func(t *testing.T) { - f := Signal{ - Sign: Signed, - Endian: LittleEndian, - RawValue: 1, - Size: 2, - } - b, err := f.Encode() - if err != nil { - t.Error("Expected nil, got", err) - } - expected := []byte{0x01, 0x00} - if !reflect.DeepEqual(b.Bytes, expected) { - t.Error("Expected", expected, "got", b.Bytes) - } - }) - t.Run("Test Other", func(t *testing.T) { - f := Signal{ - Sign: -1, - Endian: -1, - RawValue: 1, - Size: 2, - } - _, err := f.Encode() - if err == nil { - t.Error("Expected err, got nil") - } - }) -} +// func TestSignal_Encode(t *testing.T) { +// t.Run("Test Big Endian Unsigned", func(t *testing.T) { +// f := Signal{ +// Sign: Unsigned, +// Endian: BigEndian, +// RawValue: 1, +// Size: 2, +// } +// b, err := f.Encode() +// if err != nil { +// t.Error("Expected nil, got", err) +// } +// expected := []byte{0x00, 0x01} +// if !reflect.DeepEqual(b.Bytes, expected) { +// t.Error("Expected", expected, "got", b.Bytes) +// } +// }) +// t.Run("Test Big Endian Signed", func(t *testing.T) { +// f := Signal{ +// Sign: Signed, +// Endian: BigEndian, +// RawValue: 1, +// Size: 2, +// } +// b, err := f.Encode() +// if err != nil { +// t.Error("Expected nil, got", err) +// } +// expected := []byte{0x00, 0x01} +// if !reflect.DeepEqual(b.Bytes, expected) { +// t.Error("Expected", expected, "got", b.Bytes) +// } +// }) +// t.Run("Test Little Endian Unsigned", func(t *testing.T) { +// f := Signal{ +// Sign: Unsigned, +// Endian: LittleEndian, +// RawValue: 1, +// Size: 2, +// } +// b, err := f.Encode() +// if err != nil { +// t.Error("Expected nil, got", err) +// } +// expected := []byte{0x01, 0x00} +// if !reflect.DeepEqual(b.Bytes, expected) { +// t.Error("Expected", expected, "got", b.Bytes) +// } +// }) +// t.Run("Test Little Endian Signed", func(t *testing.T) { +// f := Signal{ +// Sign: Signed, +// Endian: LittleEndian, +// RawValue: 1, +// Size: 2, +// } +// b, err := f.Encode() +// if err != nil { +// t.Error("Expected nil, got", err) +// } +// expected := []byte{0x01, 0x00} +// if !reflect.DeepEqual(b.Bytes, expected) { +// t.Error("Expected", expected, "got", b.Bytes) +// } +// }) +// t.Run("Test Other", func(t *testing.T) { +// f := Signal{ +// Sign: -1, +// Endian: -1, +// RawValue: 1, +// Size: 2, +// } +// _, err := f.Encode() +// if err == nil { +// t.Error("Expected err, got nil") +// } +// }) +// } diff --git a/vehicle.go b/vehicle.go index 2bfd62f..9f008b2 100644 --- a/vehicle.go +++ b/vehicle.go @@ -20,7 +20,7 @@ type Vehicle struct { // The UploadKey is a unique identifier for the vehicle's uploaded files. // This is used to authenticate the vehicle when processing uploaded data. - UploadKey string `json:"upload_key"` + UploadKey int `json:"upload_key"` UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime;precision:6"` CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;precision:6"`