Refactor kafka to pure Go (franz-go), fix DBC stubs, update Dockerfile
This commit is contained in:
55
pkg/can-go/pkg/descriptor/database.go
Normal file
55
pkg/can-go/pkg/descriptor/database.go
Normal 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))
|
||||
}
|
||||
73
pkg/can-go/pkg/descriptor/message.go
Normal file
73
pkg/can-go/pkg/descriptor/message.go
Normal 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
|
||||
}
|
||||
37
pkg/can-go/pkg/descriptor/message_test.go
Normal file
37
pkg/can-go/pkg/descriptor/message_test.go
Normal 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))
|
||||
}
|
||||
9
pkg/can-go/pkg/descriptor/node.go
Normal file
9
pkg/can-go/pkg/descriptor/node.go
Normal 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
|
||||
}
|
||||
31
pkg/can-go/pkg/descriptor/sendtype.go
Normal file
31
pkg/can-go/pkg/descriptor/sendtype.go
Normal 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
|
||||
}
|
||||
25
pkg/can-go/pkg/descriptor/sendtype_string.go
Normal file
25
pkg/can-go/pkg/descriptor/sendtype_string.go
Normal 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]]
|
||||
}
|
||||
26
pkg/can-go/pkg/descriptor/sendtype_test.go
Normal file
26
pkg/can-go/pkg/descriptor/sendtype_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
326
pkg/can-go/pkg/descriptor/signal.go
Normal file
326
pkg/can-go/pkg/descriptor/signal.go
Normal 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
|
||||
}
|
||||
294
pkg/can-go/pkg/descriptor/signal_test.go
Normal file
294
pkg/can-go/pkg/descriptor/signal_test.go
Normal 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)
|
||||
}
|
||||
6
pkg/can-go/pkg/descriptor/valuedescription.go
Normal file
6
pkg/can-go/pkg/descriptor/valuedescription.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package descriptor
|
||||
|
||||
type ValueDescription struct {
|
||||
Value int
|
||||
Description string
|
||||
}
|
||||
Reference in New Issue
Block a user