Refactor kafka to pure Go (franz-go), fix DBC stubs, update Dockerfile

This commit is contained in:
Chris Rai
2026-01-31 00:05:47 -05:00
parent fbb820d7b3
commit b5bec57dfa
776 changed files with 18945 additions and 2052 deletions

View File

@@ -0,0 +1,55 @@
package descriptor
import (
"path"
"strings"
)
// Database represents a CAN database.
type Database struct {
// SourceFile of the database.
//
// Example:
// github.com/einride/can-databases/dbc/j1939.dbc
SourceFile string
// Version of the database.
Version string
// Messages in the database.
Messages [2048]*Message
// Nodes in the database.
Nodes []*Node
// ECUs in the database
ECUs map[string]bool
}
func (d *Database) Node(nodeName string) (*Node, bool) {
for _, n := range d.Nodes {
if n.Name == nodeName {
return n, true
}
}
return nil, false
}
func (d *Database) Message(id uint32) (*Message, bool) {
m := d.Messages[id]
return m, m != nil
}
func (d *Database) Signal(messageID uint32, signalName string) (*Signal, bool) {
message, ok := d.Message(messageID)
if !ok {
return nil, false
}
for _, s := range message.Signals {
if s.Name == signalName {
return s, true
}
}
return nil, false
}
// Description returns the name of the Database.
func (d *Database) Name() string {
return strings.TrimSuffix(path.Base(d.SourceFile), path.Ext(d.SourceFile))
}

View File

@@ -0,0 +1,73 @@
package descriptor
import (
"time"
"github.com/fiskerinc/cloud-services/pkg/can-go"
)
// Message describes a CAN message.
type Message struct {
// Description of the message.
Name string
// ID of the message.
ID uint32
// IsExtended is true if the message is an extended CAN message.
IsExtended bool
// SendType is the message's send type.
SendType SendType
// Length in bytes.
Length uint16
// Description of the message.
Description string
// Signals in the message payload.
Signals []*Signal
// SenderNode is the name of the node sending the message.
SenderNode string
// CycleTime is the cycle time of a cyclic message.
CycleTime time.Duration
// DelayTime is the allowed delay between cyclic message sends.
DelayTime time.Duration
}
// MultiplexerSignal returns the message's multiplexer signal.
func (m *Message) MultiplexerSignal() (*Signal, bool) {
for _, s := range m.Signals {
if s.IsMultiplexer {
return s, true
}
}
return nil, false
}
// Decode decodes a can Payload into a decoded signal array.
func (m *Message) Decode(p *can.Payload) []DecodedSignal {
var data can.Data
if m.Length <= 8 {
copy(data[:], p.Data)
}
numSignals := len(m.Signals)
signals := make([]DecodedSignal, numSignals)
for i, signal := range m.Signals {
var valueDesc string
var value float64
if m.Length > 8 {
valueDesc, _ = signal.UnmarshalValueDescriptionPayload(p)
value = signal.DecodePayload(p)
} else {
valueDesc, _ = signal.UnmarshalValueDescription(data)
value = signal.Decode(data)
}
s := DecodedSignal{
Value: value,
Description: valueDesc,
Signal: signal,
}
signals[i] = s
}
return signals
}

View File

@@ -0,0 +1,37 @@
package descriptor
import (
"testing"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestMessage_MultiplexerSignal(t *testing.T) {
mux := &Signal{
Name: "Mux",
IsMultiplexer: true,
}
m := &Message{
Signals: []*Signal{
{Name: "NotMux"},
mux,
{Name: "AlsoNotMux"},
},
}
actualMux, ok := m.MultiplexerSignal()
assert.Assert(t, ok)
assert.DeepEqual(t, mux, actualMux)
}
func TestMessage_MultiplexerSignal_NotFound(t *testing.T) {
m := &Message{
Signals: []*Signal{
{Name: "NotMux"},
{Name: "AlsoNotMux"},
},
}
actualMux, ok := m.MultiplexerSignal()
assert.Assert(t, !ok)
assert.Assert(t, is.Nil(actualMux))
}

View File

@@ -0,0 +1,9 @@
package descriptor
// Node describes a CAN node.
type Node struct {
// Description of the CAN node.
Name string
// Description of the CAN node.
Description string
}

View File

@@ -0,0 +1,31 @@
package descriptor
import "strings"
// SendType represents the send type of a message.
type SendType uint8
//go:generate stringer -type SendType -trimprefix SendType
const (
// SendTypeNone means the send type is unknown or not specified.
SendTypeNone SendType = iota
// SendTypeCyclic means the message is sent cyclically.
SendTypeCyclic
// SendTypeEvent means the message is only sent upon event or request.
SendTypeEvent
)
// UnmarshalString sets the value of *s from the provided string.
func (s *SendType) UnmarshalString(str string) error {
// TODO: Decide on conventions and make this more strict
switch strings.ToLower(str) {
case "cyclic", "cyclicifactive", "periodic", "fixedperiodic", "enabledperiodic", "eventperiodic":
*s = SendTypeCyclic
case "event", "onevent":
*s = SendTypeEvent
default:
*s = SendTypeNone
}
return nil
}

View File

@@ -0,0 +1,25 @@
// Code generated by "stringer -type SendType -trimprefix SendType"; DO NOT EDIT.
package descriptor
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[SendTypeNone-0]
_ = x[SendTypeCyclic-1]
_ = x[SendTypeEvent-2]
}
const _SendType_name = "NoneCyclicEvent"
var _SendType_index = [...]uint8{0, 4, 10, 15}
func (i SendType) String() string {
if i >= SendType(len(_SendType_index)-1) {
return "SendType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _SendType_name[_SendType_index[i]:_SendType_index[i+1]]
}

View File

@@ -0,0 +1,26 @@
package descriptor
import (
"testing"
"gotest.tools/v3/assert"
)
func TestSendType_UnmarshalString(t *testing.T) {
for _, tt := range []struct {
str string
expected SendType
}{
{str: "Cyclic", expected: SendTypeCyclic},
{str: "Periodic", expected: SendTypeCyclic},
{str: "OnEvent", expected: SendTypeEvent},
{str: "Event", expected: SendTypeEvent},
} {
tt := tt
t.Run(tt.str, func(t *testing.T) {
var actual SendType
assert.NilError(t, actual.UnmarshalString(tt.str))
assert.Equal(t, tt.expected, actual)
})
}
}

View File

@@ -0,0 +1,326 @@
package descriptor
import (
"math"
"github.com/fiskerinc/cloud-services/pkg/can-go"
)
// Signal describes a CAN signal.
type Signal struct {
// Description of the signal.
Name string
// Start bit.
Start uint16
// Length in bits.
Length uint16
// IsBigEndian is true if the signal is big-endian.
IsBigEndian bool
// IsSigned is true if the signal uses raw signed values.
IsSigned bool
// IsMultiplexer is true if the signal is the multiplexor of a multiplexed message.
IsMultiplexer bool
// IsMultiplexed is true if the signal is multiplexed.
IsMultiplexed bool
// MultiplexerValue is the value of the multiplexer when this signal is present.
MultiplexerValue uint
// Offset for real-world transform.
Offset float64
// Scale for real-world transform.
Scale float64
// Min real-world value.
Min float64
// Max real-world value.
Max float64
// Unit of the signal.
Unit string
// Description of the signal.
Description string
// ValueDescriptions of the signal.
ValueDescriptions []*ValueDescription
// ReceiverNodes is the list of names of the nodes receiving the signal.
ReceiverNodes []string
// DefaultValue of the signal.
DefaultValue int
}
type DecodedSignal struct {
// Value is the physical value of a decoded signal
Value float64
// Description physical description of a decoded signal
Description string
// Signal is a pointer to the dbc signal
Signal *Signal
}
// ValueDescription returns the value description for the provided value.
func (s *Signal) ValueDescription(value int) (string, bool) {
for _, vd := range s.ValueDescriptions {
if vd.Value == value {
return vd.Description, true
}
}
return "", false
}
// ToPhysical converts a raw signal value to its physical value.
func (s *Signal) ToPhysical(value float64) float64 {
result := value
result *= s.Scale
result += s.Offset
if s.Min != 0 || s.Max != 0 {
result = math.Max(math.Min(result, s.Max), s.Min)
}
return result
}
// FromPhysical converts a physical signal value to its raw value.
func (s *Signal) FromPhysical(physical float64) float64 {
result := physical
if s.Min != 0 || s.Max != 0 {
result = math.Max(math.Min(result, s.Max), s.Min)
}
result -= s.Offset
result /= s.Scale
// perform saturated cast
if s.IsSigned {
result = math.Max(float64(s.MinSigned()), math.Min(float64(s.MaxSigned()), result))
} else {
result = math.Max(0, math.Min(float64(s.MaxUnsigned()), result))
}
return result
}
// UnmarshalPhysical returns the physical value of the signal in the provided CAN frame.
func (s *Signal) UnmarshalPhysical(d can.Data) float64 {
switch {
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(uint8(s.Start), uint8(s.Length))
} else {
value = d.SignedBitsLittleEndian(uint8(s.Start), uint8(s.Length))
}
return s.ToPhysical(float64(value))
default:
var value uint64
if s.IsBigEndian {
value = d.UnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length))
} else {
value = d.UnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length))
}
return s.ToPhysical(float64(value))
}
}
// Decode returns the physical value of the signal in the provided CAN frame.
func (s *Signal) Decode(d can.Data) float64 {
switch {
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(uint8(s.Start), uint8(s.Length))
} else {
value = d.SignedBitsLittleEndian(uint8(s.Start), uint8(s.Length))
}
return s.Offset + float64(value)*s.Scale
default:
var value uint64
if s.IsBigEndian {
value = d.UnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length))
} else {
value = d.UnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length))
}
return s.Offset + float64(value)*s.Scale
}
}
// 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))
}
}
// DecodePayload returns the physical value of the signal in the provided CAN frame.
func (s *Signal) DecodePayload(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.Offset + float64(value)*s.Scale
default:
var value uint64
if s.IsBigEndian {
value = p.UnsignedBitsBigEndian(s.Start, s.Length)
} else {
value = p.UnsignedBitsLittleEndian(s.Start, s.Length)
}
return s.Offset + float64(value)*s.Scale
}
}
// 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(uint8(s.Start), uint8(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.
func (s *Signal) UnmarshalValueDescription(d can.Data) (string, bool) {
if len(s.ValueDescriptions) == 0 {
return "", false
}
var intValue int
if s.IsSigned {
intValue = int(s.UnmarshalSigned(d))
} else {
intValue = int(s.UnmarshalUnsigned(d))
}
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(uint8(s.Start), uint8(s.Length))
}
return d.SignedBitsLittleEndian(uint8(s.Start), 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 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(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(uint8(s.Start), uint8(s.Length), value)
} else {
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(uint8(s.Start), uint8(s.Length), value)
} else {
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(uint8(s.Start), value)
}
// MaxUnsigned returns the maximum unsigned value representable by the signal.
func (s *Signal) MaxUnsigned() uint64 {
return (2 << (uint8(s.Length) - 1)) - 1
}
// MinSigned returns the minimum signed value representable by the signal.
func (s *Signal) MinSigned() int64 {
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 << (uint8(s.Length) - 1) / 2) - 1
}
// SaturatedCastSigned performs a saturated cast of an int64 to the value domain of the signal.
func (s *Signal) SaturatedCastSigned(value int64) int64 {
min := s.MinSigned()
max := s.MaxSigned()
switch {
case value < min:
return min
case value > max:
return max
default:
return value
}
}
// SaturatedCastUnsigned performs a saturated cast of a uint64 to the value domain of the signal.
func (s *Signal) SaturatedCastUnsigned(value uint64) uint64 {
max := s.MaxUnsigned()
if value > max {
return max
}
return value
}

View File

@@ -0,0 +1,294 @@
package descriptor
import (
"encoding/hex"
"math"
"testing"
can "github.com/fiskerinc/cloud-services/pkg/can-go"
"gotest.tools/v3/assert"
)
func TestSignal_Decode_UnsignedBigEndian(t *testing.T) {
s := &Signal{
Name: "TestSignal",
IsSigned: false,
IsBigEndian: true,
Offset: -1,
Scale: 3.0517578125e-05,
Length: 10,
Start: 32,
Min: 0,
Max: 1,
}
const value uint64 = 180
// Testing can.Data
var data can.Data
data.SetUnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value)
actual := s.Decode(data)
assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual)
// Testing payload
p, _ := can.PayloadFromHex(hex.EncodeToString(data[:]))
actual = s.DecodePayload(&p)
assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual)
}
func TestSignal_Decode_SignedBigEndian(t *testing.T) {
s := &Signal{
Name: "TestSignal",
IsSigned: true,
IsBigEndian: true,
Offset: -1,
Scale: 3.0517578125e-05,
Length: 10,
Start: 32,
Min: -1,
Max: 1,
}
const value int64 = -180
// Testing can.Data
var data can.Data
data.SetSignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value)
actual := s.Decode(data)
assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual)
// Testing payload
p, _ := can.PayloadFromHex(hex.EncodeToString(data[:]))
actual = s.DecodePayload(&p)
assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual)
}
func TestSignal_Decode_UnsignedLittleEndian(t *testing.T) {
s := &Signal{
Name: "TestSignal",
IsSigned: false,
IsBigEndian: false,
Offset: -1,
Scale: 3.0517578125e-05,
Length: 10,
Start: 32,
Min: 0,
Max: 1,
}
const value uint64 = 180
// Testing can.Data
var data can.Data
data.SetUnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length), value)
actual := s.Decode(data)
assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual)
// Testing payload
p, _ := can.PayloadFromHex(hex.EncodeToString(data[:]))
actual = s.DecodePayload(&p)
assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual)
}
func TestSignal_Decode_SignedLittleEndian(t *testing.T) {
s := &Signal{
Name: "TestSignal",
IsSigned: true,
IsBigEndian: false,
Offset: -1,
Scale: 3.0517578125e-05,
Length: 10,
Start: 32,
Min: -1,
Max: 1,
}
const value int64 = -180
// Testing can.Data
var data can.Data
data.SetSignedBitsLittleEndian(uint8(s.Start), uint8(s.Length), value)
actual := s.Decode(data)
assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual)
// Testing payload
p, _ := can.PayloadFromHex(hex.EncodeToString(data[:]))
actual = s.DecodePayload(&p)
assert.DeepEqual(t, s.Offset+float64(value)*s.Scale, actual)
}
func TestSignal_UnmarshalPhysical_UnsignedBigEndian(t *testing.T) {
s := &Signal{
Name: "TestSignal",
IsSigned: false,
IsBigEndian: true,
Offset: -1,
Scale: 0.5,
Length: 10,
Start: 32,
Min: 0,
Max: 50,
}
const value uint64 = 180
// Testing can.Data
var data can.Data
data.SetUnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value)
actual := s.UnmarshalPhysical(data)
assert.DeepEqual(t, s.Max, actual)
// Testing payload
p, _ := can.PayloadFromHex(hex.EncodeToString(data[:]))
actual = s.UnmarshalPhysicalPayload(&p)
assert.DeepEqual(t, s.Max, actual)
}
func TestSignal_UnmarshalPhysical_SignedBigEndian(t *testing.T) {
s := &Signal{
Name: "TestSignal",
IsSigned: true,
IsBigEndian: true,
Offset: -1,
Scale: 0.5,
Length: 10,
Start: 32,
Min: -50,
Max: 0,
}
const value int64 = -180
// Testing can.Data
var data can.Data
data.SetSignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value)
actual := s.UnmarshalPhysical(data)
assert.DeepEqual(t, s.Min, actual)
// Testing payload
p, _ := can.PayloadFromHex(hex.EncodeToString(data[:]))
actual = s.UnmarshalPhysicalPayload(&p)
assert.DeepEqual(t, s.Min, actual)
}
func TestSignal_UnmarshalPhysical_UnsignedLittleEndian(t *testing.T) {
s := &Signal{
Name: "TestSignal",
IsSigned: false,
IsBigEndian: false,
Offset: -1,
Scale: 0.5,
Length: 10,
Start: 32,
Min: 0,
Max: 50,
}
const value uint64 = 180
// Testing can.Data
var data can.Data
data.SetUnsignedBitsLittleEndian(uint8(s.Start), uint8(s.Length), value)
actual := s.UnmarshalPhysical(data)
assert.DeepEqual(t, s.Max, actual)
// Testing payload
p, _ := can.PayloadFromHex(hex.EncodeToString(data[:]))
actual = s.UnmarshalPhysicalPayload(&p)
assert.DeepEqual(t, s.Max, actual)
}
func TestSignal_UnmarshalPhysical_SignedLittleEndian(t *testing.T) {
s := &Signal{
Name: "TestSignal",
IsSigned: true,
IsBigEndian: false,
Offset: -1,
Scale: 0.5,
Length: 10,
Start: 32,
Min: -50,
Max: 0,
}
const value int64 = -180
// Testing can.Data
var data can.Data
data.SetSignedBitsLittleEndian(uint8(s.Start), uint8(s.Length), value)
actual := s.UnmarshalPhysical(data)
assert.DeepEqual(t, s.Min, actual)
// Testing payload
p, _ := can.PayloadFromHex(hex.EncodeToString(data[:]))
actual = s.UnmarshalPhysicalPayload(&p)
assert.DeepEqual(t, s.Min, actual)
}
func TestSignal_FromPhysical_SaturatedCast(t *testing.T) {
s := &Signal{
Name: "TestSignal",
Offset: -1,
Scale: 3.0517578125e-05,
Min: -1,
Max: 1,
Length: 16,
}
// without a saturated cast, the result would be math.MaxUint16 + 1, which would wrap around to 0
assert.Equal(t, uint16(math.MaxUint16), uint16(s.FromPhysical(180)))
}
func TestSignal_SaturatedCastSigned(t *testing.T) {
s := &Signal{
Name: "TestSignal",
IsSigned: true,
Length: 6,
}
assert.Equal(t, int64(31), s.SaturatedCastSigned(254))
assert.Equal(t, int64(-32), s.SaturatedCastSigned(-255))
}
func TestSignal_SaturatedCastUnsigned(t *testing.T) {
s := &Signal{
Name: "TestSignal",
Length: 6,
}
assert.Equal(t, uint64(63), s.SaturatedCastUnsigned(255))
}
func TestSignal_UnmarshalSigned_BigEndian(t *testing.T) {
s := &Signal{
Name: "TestSignal",
IsSigned: true,
IsBigEndian: true,
Length: 8,
Start: 32,
}
const value int64 = -8
var data can.Data
data.SetSignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value)
assert.Equal(t, value, s.UnmarshalSigned(data))
}
func TestSignal_MarshalUnsigned_BigEndian(t *testing.T) {
s := &Signal{
Name: "TestSignal",
IsBigEndian: true,
Length: 8,
Start: 32,
}
const value uint64 = 8
var expected can.Data
expected.SetUnsignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value)
var actual can.Data
s.MarshalUnsigned(&actual, value)
assert.DeepEqual(t, expected, actual)
}
func TestSignal_MarshalSigned_BigEndian(t *testing.T) {
s := &Signal{
Name: "TestSignal",
IsSigned: true,
IsBigEndian: true,
Length: 8,
Start: 32,
}
const value int64 = -8
var expected can.Data
expected.SetSignedBitsBigEndian(uint8(s.Start), uint8(s.Length), value)
var actual can.Data
s.MarshalSigned(&actual, value)
assert.DeepEqual(t, expected, actual)
}

View File

@@ -0,0 +1,6 @@
package descriptor
type ValueDescription struct {
Value int
Description string
}