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,27 @@
// Package clock provides primitives for mocking time.
package clock
import (
"time"
)
// Clock provides capabilities from the time standard library package.
type Clock interface {
// After waits for the duration to elapse and then sends the current time on the returned channel.
After(duration time.Duration) <-chan time.Time
// NewTicker returns a new Ticker.
NewTicker(d time.Duration) Ticker
// Now returns the current local time.
Now() time.Time
}
// Ticker wraps the time.Ticker class.
type Ticker interface {
// C returns the channel on which the ticks are delivered.
C() <-chan time.Time
// Stop the Ticker.
Stop()
}

View File

@@ -0,0 +1,34 @@
package clock
import (
"time"
)
// System returns a Clock implementation that delegate to the time package.
func System() Clock {
return &systemClock{}
}
type systemClock struct{}
var _ Clock = &systemClock{}
func (c systemClock) After(d time.Duration) <-chan time.Time {
return time.After(d)
}
func (c systemClock) NewTicker(d time.Duration) Ticker {
return &systemTicker{Ticker: *time.NewTicker(d)}
}
func (c systemClock) Now() time.Time {
return time.Now()
}
type systemTicker struct {
time.Ticker
}
func (t systemTicker) C() <-chan time.Time {
return t.Ticker.C
}

View File

@@ -0,0 +1,529 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/fiskerinc/cloud-services/pkg/can-go/pkg/canrunner (interfaces: Node,TransmittedMessage,ReceivedMessage,FrameTransmitter,FrameReceiver)
// Package mockcanrunner is a generated GoMock package.
package mockcanrunner
import (
context "context"
can "github.com/fiskerinc/cloud-services/pkg/can-go"
canrunner "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/canrunner"
descriptor "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor"
gomock "github.com/golang/mock/gomock"
net "net"
reflect "reflect"
time "time"
)
// MockNode is a mock of Node interface.
type MockNode struct {
ctrl *gomock.Controller
recorder *MockNodeMockRecorder
}
// MockNodeMockRecorder is the mock recorder for MockNode.
type MockNodeMockRecorder struct {
mock *MockNode
}
// NewMockNode creates a new mock instance.
func NewMockNode(ctrl *gomock.Controller) *MockNode {
mock := &MockNode{ctrl: ctrl}
mock.recorder = &MockNodeMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockNode) EXPECT() *MockNodeMockRecorder {
return m.recorder
}
// Connect mocks base method.
func (m *MockNode) Connect() (net.Conn, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Connect")
ret0, _ := ret[0].(net.Conn)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Connect indicates an expected call of Connect.
func (mr *MockNodeMockRecorder) Connect() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockNode)(nil).Connect))
}
// Descriptor mocks base method.
func (m *MockNode) Descriptor() *descriptor.Node {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Descriptor")
ret0, _ := ret[0].(*descriptor.Node)
return ret0
}
// Descriptor indicates an expected call of Descriptor.
func (mr *MockNodeMockRecorder) Descriptor() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Descriptor", reflect.TypeOf((*MockNode)(nil).Descriptor))
}
// Lock mocks base method.
func (m *MockNode) Lock() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Lock")
}
// Lock indicates an expected call of Lock.
func (mr *MockNodeMockRecorder) Lock() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Lock", reflect.TypeOf((*MockNode)(nil).Lock))
}
// ReceivedMessage mocks base method.
func (m *MockNode) ReceivedMessage(arg0 uint32) (canrunner.ReceivedMessage, bool) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ReceivedMessage", arg0)
ret0, _ := ret[0].(canrunner.ReceivedMessage)
ret1, _ := ret[1].(bool)
return ret0, ret1
}
// ReceivedMessage indicates an expected call of ReceivedMessage.
func (mr *MockNodeMockRecorder) ReceivedMessage(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedMessage", reflect.TypeOf((*MockNode)(nil).ReceivedMessage), arg0)
}
// TransmittedMessages mocks base method.
func (m *MockNode) TransmittedMessages() []canrunner.TransmittedMessage {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "TransmittedMessages")
ret0, _ := ret[0].([]canrunner.TransmittedMessage)
return ret0
}
// TransmittedMessages indicates an expected call of TransmittedMessages.
func (mr *MockNodeMockRecorder) TransmittedMessages() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransmittedMessages", reflect.TypeOf((*MockNode)(nil).TransmittedMessages))
}
// Unlock mocks base method.
func (m *MockNode) Unlock() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Unlock")
}
// Unlock indicates an expected call of Unlock.
func (mr *MockNodeMockRecorder) Unlock() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unlock", reflect.TypeOf((*MockNode)(nil).Unlock))
}
// MockTransmittedMessage is a mock of TransmittedMessage interface.
type MockTransmittedMessage struct {
ctrl *gomock.Controller
recorder *MockTransmittedMessageMockRecorder
}
// MockTransmittedMessageMockRecorder is the mock recorder for MockTransmittedMessage.
type MockTransmittedMessageMockRecorder struct {
mock *MockTransmittedMessage
}
// NewMockTransmittedMessage creates a new mock instance.
func NewMockTransmittedMessage(ctrl *gomock.Controller) *MockTransmittedMessage {
mock := &MockTransmittedMessage{ctrl: ctrl}
mock.recorder = &MockTransmittedMessageMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockTransmittedMessage) EXPECT() *MockTransmittedMessageMockRecorder {
return m.recorder
}
// BeforeTransmitHook mocks base method.
func (m *MockTransmittedMessage) BeforeTransmitHook() func(context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BeforeTransmitHook")
ret0, _ := ret[0].(func(context.Context) error)
return ret0
}
// BeforeTransmitHook indicates an expected call of BeforeTransmitHook.
func (mr *MockTransmittedMessageMockRecorder) BeforeTransmitHook() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeforeTransmitHook", reflect.TypeOf((*MockTransmittedMessage)(nil).BeforeTransmitHook))
}
// Descriptor mocks base method.
func (m *MockTransmittedMessage) Descriptor() *descriptor.Message {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Descriptor")
ret0, _ := ret[0].(*descriptor.Message)
return ret0
}
// Descriptor indicates an expected call of Descriptor.
func (mr *MockTransmittedMessageMockRecorder) Descriptor() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Descriptor", reflect.TypeOf((*MockTransmittedMessage)(nil).Descriptor))
}
// Frame mocks base method.
func (m *MockTransmittedMessage) Frame() can.Frame {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Frame")
ret0, _ := ret[0].(can.Frame)
return ret0
}
// Frame indicates an expected call of Frame.
func (mr *MockTransmittedMessageMockRecorder) Frame() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Frame", reflect.TypeOf((*MockTransmittedMessage)(nil).Frame))
}
// IsCyclicTransmissionEnabled mocks base method.
func (m *MockTransmittedMessage) IsCyclicTransmissionEnabled() bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "IsCyclicTransmissionEnabled")
ret0, _ := ret[0].(bool)
return ret0
}
// IsCyclicTransmissionEnabled indicates an expected call of IsCyclicTransmissionEnabled.
func (mr *MockTransmittedMessageMockRecorder) IsCyclicTransmissionEnabled() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsCyclicTransmissionEnabled", reflect.TypeOf((*MockTransmittedMessage)(nil).IsCyclicTransmissionEnabled))
}
// MarshalFrame mocks base method.
func (m *MockTransmittedMessage) MarshalFrame() (can.Frame, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "MarshalFrame")
ret0, _ := ret[0].(can.Frame)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// MarshalFrame indicates an expected call of MarshalFrame.
func (mr *MockTransmittedMessageMockRecorder) MarshalFrame() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarshalFrame", reflect.TypeOf((*MockTransmittedMessage)(nil).MarshalFrame))
}
// Reset mocks base method.
func (m *MockTransmittedMessage) Reset() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Reset")
}
// Reset indicates an expected call of Reset.
func (mr *MockTransmittedMessageMockRecorder) Reset() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reset", reflect.TypeOf((*MockTransmittedMessage)(nil).Reset))
}
// SetTransmitTime mocks base method.
func (m *MockTransmittedMessage) SetTransmitTime(arg0 time.Time) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetTransmitTime", arg0)
}
// SetTransmitTime indicates an expected call of SetTransmitTime.
func (mr *MockTransmittedMessageMockRecorder) SetTransmitTime(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTransmitTime", reflect.TypeOf((*MockTransmittedMessage)(nil).SetTransmitTime), arg0)
}
// String mocks base method.
func (m *MockTransmittedMessage) String() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "String")
ret0, _ := ret[0].(string)
return ret0
}
// String indicates an expected call of String.
func (mr *MockTransmittedMessageMockRecorder) String() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockTransmittedMessage)(nil).String))
}
// TransmitEventChan mocks base method.
func (m *MockTransmittedMessage) TransmitEventChan() <-chan struct{} {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "TransmitEventChan")
ret0, _ := ret[0].(<-chan struct{})
return ret0
}
// TransmitEventChan indicates an expected call of TransmitEventChan.
func (mr *MockTransmittedMessageMockRecorder) TransmitEventChan() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransmitEventChan", reflect.TypeOf((*MockTransmittedMessage)(nil).TransmitEventChan))
}
// UnmarshalFrame mocks base method.
func (m *MockTransmittedMessage) UnmarshalFrame(arg0 can.Frame) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UnmarshalFrame", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// UnmarshalFrame indicates an expected call of UnmarshalFrame.
func (mr *MockTransmittedMessageMockRecorder) UnmarshalFrame(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnmarshalFrame", reflect.TypeOf((*MockTransmittedMessage)(nil).UnmarshalFrame), arg0)
}
// WakeUpChan mocks base method.
func (m *MockTransmittedMessage) WakeUpChan() <-chan struct{} {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "WakeUpChan")
ret0, _ := ret[0].(<-chan struct{})
return ret0
}
// WakeUpChan indicates an expected call of WakeUpChan.
func (mr *MockTransmittedMessageMockRecorder) WakeUpChan() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WakeUpChan", reflect.TypeOf((*MockTransmittedMessage)(nil).WakeUpChan))
}
// MockReceivedMessage is a mock of ReceivedMessage interface.
type MockReceivedMessage struct {
ctrl *gomock.Controller
recorder *MockReceivedMessageMockRecorder
}
// MockReceivedMessageMockRecorder is the mock recorder for MockReceivedMessage.
type MockReceivedMessageMockRecorder struct {
mock *MockReceivedMessage
}
// NewMockReceivedMessage creates a new mock instance.
func NewMockReceivedMessage(ctrl *gomock.Controller) *MockReceivedMessage {
mock := &MockReceivedMessage{ctrl: ctrl}
mock.recorder = &MockReceivedMessageMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockReceivedMessage) EXPECT() *MockReceivedMessageMockRecorder {
return m.recorder
}
// AfterReceiveHook mocks base method.
func (m *MockReceivedMessage) AfterReceiveHook() func(context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AfterReceiveHook")
ret0, _ := ret[0].(func(context.Context) error)
return ret0
}
// AfterReceiveHook indicates an expected call of AfterReceiveHook.
func (mr *MockReceivedMessageMockRecorder) AfterReceiveHook() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AfterReceiveHook", reflect.TypeOf((*MockReceivedMessage)(nil).AfterReceiveHook))
}
// Descriptor mocks base method.
func (m *MockReceivedMessage) Descriptor() *descriptor.Message {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Descriptor")
ret0, _ := ret[0].(*descriptor.Message)
return ret0
}
// Descriptor indicates an expected call of Descriptor.
func (mr *MockReceivedMessageMockRecorder) Descriptor() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Descriptor", reflect.TypeOf((*MockReceivedMessage)(nil).Descriptor))
}
// Frame mocks base method.
func (m *MockReceivedMessage) Frame() can.Frame {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Frame")
ret0, _ := ret[0].(can.Frame)
return ret0
}
// Frame indicates an expected call of Frame.
func (mr *MockReceivedMessageMockRecorder) Frame() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Frame", reflect.TypeOf((*MockReceivedMessage)(nil).Frame))
}
// MarshalFrame mocks base method.
func (m *MockReceivedMessage) MarshalFrame() (can.Frame, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "MarshalFrame")
ret0, _ := ret[0].(can.Frame)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// MarshalFrame indicates an expected call of MarshalFrame.
func (mr *MockReceivedMessageMockRecorder) MarshalFrame() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarshalFrame", reflect.TypeOf((*MockReceivedMessage)(nil).MarshalFrame))
}
// Reset mocks base method.
func (m *MockReceivedMessage) Reset() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Reset")
}
// Reset indicates an expected call of Reset.
func (mr *MockReceivedMessageMockRecorder) Reset() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reset", reflect.TypeOf((*MockReceivedMessage)(nil).Reset))
}
// SetReceiveTime mocks base method.
func (m *MockReceivedMessage) SetReceiveTime(arg0 time.Time) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "SetReceiveTime", arg0)
}
// SetReceiveTime indicates an expected call of SetReceiveTime.
func (mr *MockReceivedMessageMockRecorder) SetReceiveTime(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReceiveTime", reflect.TypeOf((*MockReceivedMessage)(nil).SetReceiveTime), arg0)
}
// String mocks base method.
func (m *MockReceivedMessage) String() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "String")
ret0, _ := ret[0].(string)
return ret0
}
// String indicates an expected call of String.
func (mr *MockReceivedMessageMockRecorder) String() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockReceivedMessage)(nil).String))
}
// UnmarshalFrame mocks base method.
func (m *MockReceivedMessage) UnmarshalFrame(arg0 can.Frame) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UnmarshalFrame", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// UnmarshalFrame indicates an expected call of UnmarshalFrame.
func (mr *MockReceivedMessageMockRecorder) UnmarshalFrame(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnmarshalFrame", reflect.TypeOf((*MockReceivedMessage)(nil).UnmarshalFrame), arg0)
}
// MockFrameTransmitter is a mock of FrameTransmitter interface.
type MockFrameTransmitter struct {
ctrl *gomock.Controller
recorder *MockFrameTransmitterMockRecorder
}
// MockFrameTransmitterMockRecorder is the mock recorder for MockFrameTransmitter.
type MockFrameTransmitterMockRecorder struct {
mock *MockFrameTransmitter
}
// NewMockFrameTransmitter creates a new mock instance.
func NewMockFrameTransmitter(ctrl *gomock.Controller) *MockFrameTransmitter {
mock := &MockFrameTransmitter{ctrl: ctrl}
mock.recorder = &MockFrameTransmitterMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockFrameTransmitter) EXPECT() *MockFrameTransmitterMockRecorder {
return m.recorder
}
// TransmitFrame mocks base method.
func (m *MockFrameTransmitter) TransmitFrame(arg0 context.Context, arg1 can.Frame) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "TransmitFrame", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// TransmitFrame indicates an expected call of TransmitFrame.
func (mr *MockFrameTransmitterMockRecorder) TransmitFrame(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransmitFrame", reflect.TypeOf((*MockFrameTransmitter)(nil).TransmitFrame), arg0, arg1)
}
// MockFrameReceiver is a mock of FrameReceiver interface.
type MockFrameReceiver struct {
ctrl *gomock.Controller
recorder *MockFrameReceiverMockRecorder
}
// MockFrameReceiverMockRecorder is the mock recorder for MockFrameReceiver.
type MockFrameReceiverMockRecorder struct {
mock *MockFrameReceiver
}
// NewMockFrameReceiver creates a new mock instance.
func NewMockFrameReceiver(ctrl *gomock.Controller) *MockFrameReceiver {
mock := &MockFrameReceiver{ctrl: ctrl}
mock.recorder = &MockFrameReceiverMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockFrameReceiver) EXPECT() *MockFrameReceiverMockRecorder {
return m.recorder
}
// Err mocks base method.
func (m *MockFrameReceiver) Err() error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Err")
ret0, _ := ret[0].(error)
return ret0
}
// Err indicates an expected call of Err.
func (mr *MockFrameReceiverMockRecorder) Err() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Err", reflect.TypeOf((*MockFrameReceiver)(nil).Err))
}
// Frame mocks base method.
func (m *MockFrameReceiver) Frame() can.Frame {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Frame")
ret0, _ := ret[0].(can.Frame)
return ret0
}
// Frame indicates an expected call of Frame.
func (mr *MockFrameReceiverMockRecorder) Frame() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Frame", reflect.TypeOf((*MockFrameReceiver)(nil).Frame))
}
// Receive mocks base method.
func (m *MockFrameReceiver) Receive() bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Receive")
ret0, _ := ret[0].(bool)
return ret0
}
// Receive indicates an expected call of Receive.
func (mr *MockFrameReceiverMockRecorder) Receive() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Receive", reflect.TypeOf((*MockFrameReceiver)(nil).Receive))
}

View File

@@ -0,0 +1,126 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/fiskerinc/cloud-services/pkg/can-go/internal/clock (interfaces: Clock,Ticker)
// Package mockclock is a generated GoMock package.
package mockclock
import (
clock "github.com/fiskerinc/cloud-services/pkg/can-go/internal/clock"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
time "time"
)
// MockClock is a mock of Clock interface.
type MockClock struct {
ctrl *gomock.Controller
recorder *MockClockMockRecorder
}
// MockClockMockRecorder is the mock recorder for MockClock.
type MockClockMockRecorder struct {
mock *MockClock
}
// NewMockClock creates a new mock instance.
func NewMockClock(ctrl *gomock.Controller) *MockClock {
mock := &MockClock{ctrl: ctrl}
mock.recorder = &MockClockMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockClock) EXPECT() *MockClockMockRecorder {
return m.recorder
}
// After mocks base method.
func (m *MockClock) After(arg0 time.Duration) <-chan time.Time {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "After", arg0)
ret0, _ := ret[0].(<-chan time.Time)
return ret0
}
// After indicates an expected call of After.
func (mr *MockClockMockRecorder) After(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "After", reflect.TypeOf((*MockClock)(nil).After), arg0)
}
// NewTicker mocks base method.
func (m *MockClock) NewTicker(arg0 time.Duration) clock.Ticker {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "NewTicker", arg0)
ret0, _ := ret[0].(clock.Ticker)
return ret0
}
// NewTicker indicates an expected call of NewTicker.
func (mr *MockClockMockRecorder) NewTicker(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewTicker", reflect.TypeOf((*MockClock)(nil).NewTicker), arg0)
}
// Now mocks base method.
func (m *MockClock) Now() time.Time {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Now")
ret0, _ := ret[0].(time.Time)
return ret0
}
// Now indicates an expected call of Now.
func (mr *MockClockMockRecorder) Now() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Now", reflect.TypeOf((*MockClock)(nil).Now))
}
// MockTicker is a mock of Ticker interface.
type MockTicker struct {
ctrl *gomock.Controller
recorder *MockTickerMockRecorder
}
// MockTickerMockRecorder is the mock recorder for MockTicker.
type MockTickerMockRecorder struct {
mock *MockTicker
}
// NewMockTicker creates a new mock instance.
func NewMockTicker(ctrl *gomock.Controller) *MockTicker {
mock := &MockTicker{ctrl: ctrl}
mock.recorder = &MockTickerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockTicker) EXPECT() *MockTickerMockRecorder {
return m.recorder
}
// C mocks base method.
func (m *MockTicker) C() <-chan time.Time {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "C")
ret0, _ := ret[0].(<-chan time.Time)
return ret0
}
// C indicates an expected call of C.
func (mr *MockTickerMockRecorder) C() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "C", reflect.TypeOf((*MockTicker)(nil).C))
}
// Stop mocks base method.
func (m *MockTicker) Stop() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Stop")
}
// Stop indicates an expected call of Stop.
func (mr *MockTickerMockRecorder) Stop() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockTicker)(nil).Stop))
}

View File

@@ -0,0 +1,120 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: pkg/socketcan/fileconn.go
// Package mocksocketcan is a generated GoMock package.
package mocksocketcan
import (
gomock "github.com/golang/mock/gomock"
reflect "reflect"
time "time"
)
// Mockfile is a mock of file interface.
type Mockfile struct {
ctrl *gomock.Controller
recorder *MockfileMockRecorder
}
// MockfileMockRecorder is the mock recorder for Mockfile.
type MockfileMockRecorder struct {
mock *Mockfile
}
// NewMockfile creates a new mock instance.
func NewMockfile(ctrl *gomock.Controller) *Mockfile {
mock := &Mockfile{ctrl: ctrl}
mock.recorder = &MockfileMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *Mockfile) EXPECT() *MockfileMockRecorder {
return m.recorder
}
// Read mocks base method.
func (m *Mockfile) Read(arg0 []byte) (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Read", arg0)
ret0, _ := ret[0].(int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Read indicates an expected call of Read.
func (mr *MockfileMockRecorder) Read(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*Mockfile)(nil).Read), arg0)
}
// Write mocks base method.
func (m *Mockfile) Write(arg0 []byte) (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Write", arg0)
ret0, _ := ret[0].(int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Write indicates an expected call of Write.
func (mr *MockfileMockRecorder) Write(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*Mockfile)(nil).Write), arg0)
}
// SetDeadline mocks base method.
func (m *Mockfile) SetDeadline(arg0 time.Time) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetDeadline", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// SetDeadline indicates an expected call of SetDeadline.
func (mr *MockfileMockRecorder) SetDeadline(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDeadline", reflect.TypeOf((*Mockfile)(nil).SetDeadline), arg0)
}
// SetReadDeadline mocks base method.
func (m *Mockfile) SetReadDeadline(arg0 time.Time) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetReadDeadline", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// SetReadDeadline indicates an expected call of SetReadDeadline.
func (mr *MockfileMockRecorder) SetReadDeadline(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadDeadline", reflect.TypeOf((*Mockfile)(nil).SetReadDeadline), arg0)
}
// SetWriteDeadline mocks base method.
func (m *Mockfile) SetWriteDeadline(arg0 time.Time) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetWriteDeadline", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// SetWriteDeadline indicates an expected call of SetWriteDeadline.
func (mr *MockfileMockRecorder) SetWriteDeadline(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteDeadline", reflect.TypeOf((*Mockfile)(nil).SetWriteDeadline), arg0)
}
// Close mocks base method.
func (m *Mockfile) Close() error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Close")
ret0, _ := ret[0].(error)
return ret0
}
// Close indicates an expected call of Close.
func (mr *MockfileMockRecorder) Close() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*Mockfile)(nil).Close))
}

View File

@@ -0,0 +1,231 @@
package generate
import (
"crypto/sha256"
"fmt"
"regexp"
"sort"
"time"
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc"
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor"
)
type CompileResult struct {
Hash string
Database *descriptor.Database
Warnings []error
}
func Compile(sourceFile string, data []byte) (result *CompileResult, err error) {
p := dbc.NewParser(sourceFile, data)
if err := p.Parse(); err != nil {
return nil, fmt.Errorf("failed to parse DBC source file: %w", err)
}
defs := p.Defs()
c := &compiler{
db: &descriptor.Database{SourceFile: sourceFile, ECUs: make(map[string]bool)},
defs: defs,
}
c.collectDescriptors()
c.addMetadata()
c.sortDescriptors()
hash := ComputeHash(data)
return &CompileResult{Hash: hash, Database: c.db, Warnings: c.warnings}, nil
}
type compileError struct {
def dbc.Def
reason string
}
func (e *compileError) Error() string {
return fmt.Sprintf("failed to compile: %v (%v)", e.reason, e.def)
}
type compiler struct {
db *descriptor.Database
defs []dbc.Def
warnings []error
}
func (c *compiler) addWarning(warning error) {
c.warnings = append(c.warnings, warning)
}
func (c *compiler) collectDescriptors() {
// find ECU names from messages
re := regexp.MustCompile(`^[0-9A-Za-z]+`)
for _, def := range c.defs {
switch def := def.(type) {
case *dbc.VersionDef:
c.db.Version = def.Version
case *dbc.MessageDef:
if def.MessageID == dbc.IndependentSignalsMessageID {
continue // don't compile
}
// add ECU names to set
ecu := re.FindString(string(def.Name))
if ecu != "" {
c.db.ECUs[ecu] = true
}
message := &descriptor.Message{
Name: string(def.Name),
ID: def.MessageID.ToCAN(),
IsExtended: def.MessageID.IsExtended(),
Length: uint16(def.Size),
SenderNode: string(def.Transmitter),
}
for _, signalDef := range def.Signals {
signal := &descriptor.Signal{
Name: string(signalDef.Name),
IsBigEndian: signalDef.IsBigEndian,
IsSigned: signalDef.IsSigned,
IsMultiplexer: signalDef.IsMultiplexerSwitch,
IsMultiplexed: signalDef.IsMultiplexed,
MultiplexerValue: uint(signalDef.MultiplexerSwitch),
Start: uint16(signalDef.StartBit),
Length: uint16(signalDef.Size),
Scale: signalDef.Factor,
Offset: signalDef.Offset,
Min: signalDef.Minimum,
Max: signalDef.Maximum,
Unit: signalDef.Unit,
}
for _, receiver := range signalDef.Receivers {
signal.ReceiverNodes = append(signal.ReceiverNodes, string(receiver))
}
message.Signals = append(message.Signals, signal)
}
c.db.Messages[message.ID] = message
case *dbc.NodesDef:
for _, node := range def.NodeNames {
c.db.Nodes = append(c.db.Nodes, &descriptor.Node{Name: string(node)})
}
}
}
}
func (c *compiler) addMetadata() {
for _, def := range c.defs {
switch def := def.(type) {
case *dbc.CommentDef:
switch def.ObjectType {
case dbc.ObjectTypeMessage:
if def.MessageID == dbc.IndependentSignalsMessageID {
continue // don't compile
}
message, ok := c.db.Message(def.MessageID.ToCAN())
if !ok {
c.addWarning(&compileError{def: def, reason: "no declared message"})
continue
}
message.Description = def.Comment
case dbc.ObjectTypeSignal:
if def.MessageID == dbc.IndependentSignalsMessageID {
continue // don't compile
}
signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName))
if !ok {
c.addWarning(&compileError{def: def, reason: "no declared signal"})
continue
}
signal.Description = def.Comment
case dbc.ObjectTypeNetworkNode:
node, ok := c.db.Node(string(def.NodeName))
if !ok {
c.addWarning(&compileError{def: def, reason: "no declared node"})
continue
}
node.Description = def.Comment
}
case *dbc.ValueDescriptionsDef:
if def.MessageID == dbc.IndependentSignalsMessageID {
continue // don't compile
}
if def.ObjectType != dbc.ObjectTypeSignal {
continue // don't compile
}
signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName))
if !ok {
c.addWarning(&compileError{def: def, reason: "no declared signal"})
continue
}
for _, valueDescription := range def.ValueDescriptions {
signal.ValueDescriptions = append(signal.ValueDescriptions, &descriptor.ValueDescription{
Description: valueDescription.Description,
Value: int(valueDescription.Value),
})
}
case *dbc.AttributeValueForObjectDef:
switch def.ObjectType {
case dbc.ObjectTypeMessage:
msg, ok := c.db.Message(def.MessageID.ToCAN())
if !ok {
c.addWarning(&compileError{def: def, reason: "no declared message"})
continue
}
switch def.AttributeName {
case "GenMsgSendType":
if err := msg.SendType.UnmarshalString(def.StringValue); err != nil {
c.addWarning(&compileError{def: def, reason: err.Error()})
continue
}
case "GenMsgCycleTime":
msg.CycleTime = time.Duration(def.IntValue) * time.Millisecond
case "GenMsgDelayTime":
msg.DelayTime = time.Duration(def.IntValue) * time.Millisecond
}
case dbc.ObjectTypeSignal:
sig, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName))
if !ok {
c.addWarning(&compileError{def: def, reason: "no declared signal"})
}
if def.AttributeName == "GenSigStartValue" {
sig.DefaultValue = int(def.IntValue)
}
}
}
}
}
func (c *compiler) sortDescriptors() {
// Sort nodes by name
sort.Slice(c.db.Nodes, func(i, j int) bool {
return c.db.Nodes[i].Name < c.db.Nodes[j].Name
})
// Sort messages by ID
// sort.Slice(c.db.Messages, func(i, j int) bool {
// return c.db.Messages[i].ID < c.db.Messages[j].ID
// })
for _, m := range c.db.Messages {
if m == nil {
continue
}
m := m
// Sort signals by start (and multiplexer value)
sort.Slice(m.Signals, func(j, k int) bool {
if m.Signals[j].MultiplexerValue < m.Signals[k].MultiplexerValue {
return true
}
return m.Signals[j].Start < m.Signals[k].Start
})
// Sort value descriptions by value
for _, s := range m.Signals {
s := s
sort.Slice(s.ValueDescriptions, func(k, l int) bool {
return s.ValueDescriptions[k].Value < s.ValueDescriptions[l].Value
})
}
}
}
func ComputeHash(input []byte) string {
h := sha256.New()
h.Write(input)
return fmt.Sprintf("%x", h.Sum(nil))
}

View File

@@ -0,0 +1,306 @@
package generate
import (
"io/ioutil"
"testing"
"time"
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor"
examplecan "github.com/fiskerinc/cloud-services/pkg/can-go/testdata/gen/go/example"
"gotest.tools/v3/assert"
)
func TestCompile_ExampleDBC(t *testing.T) {
finish := runTestInDir(t, "../..")
defer finish()
const exampleDBCFile = "testdata/dbc/example/example.dbc"
exampleDatabase := &descriptor.Database{
SourceFile: exampleDBCFile,
Version: "",
Nodes: []*descriptor.Node{
{
Name: "DBG",
},
{
Name: "DRIVER",
Description: "The driver controller driving the car",
},
{
Name: "IO",
},
{
Name: "MOTOR",
Description: "The motor controller of the car",
},
{
Name: "SENSOR",
Description: "The sensor controller of the car",
},
},
}
exampleDatabase.Messages[1] = &descriptor.Message{
ID: 1,
Name: "EmptyMessage",
SenderNode: "DBG",
}
exampleDatabase.Messages[100] = &descriptor.Message{
ID: 100,
Name: "DriverHeartbeat",
Length: 1,
SenderNode: "DRIVER",
Description: "Sync message used to synchronize the controllers",
SendType: descriptor.SendTypeCyclic,
CycleTime: time.Second,
Signals: []*descriptor.Signal{
{
Name: "Command",
Start: 0,
Length: 8,
Scale: 1,
ReceiverNodes: []string{"SENSOR", "MOTOR"},
ValueDescriptions: []*descriptor.ValueDescription{
{Value: 0, Description: "None"},
{Value: 1, Description: "Sync"},
{Value: 2, Description: "Reboot"},
},
},
},
}
exampleDatabase.Messages[101] = &descriptor.Message{
ID: 101,
Name: "MotorCommand",
Length: 1,
SenderNode: "DRIVER",
SendType: descriptor.SendTypeCyclic,
CycleTime: 100 * time.Millisecond,
Signals: []*descriptor.Signal{
{
Name: "Steer",
Start: 0,
Length: 4,
IsSigned: true,
Scale: 1,
Offset: -5,
Min: -5,
Max: 5,
ReceiverNodes: []string{"MOTOR"},
},
{
Name: "Drive",
Start: 4,
Length: 4,
Scale: 1,
Max: 9,
ReceiverNodes: []string{"MOTOR"},
},
},
}
exampleDatabase.Messages[200] = &descriptor.Message{
ID: 200,
Name: "SensorSonars",
Length: 8,
SenderNode: "SENSOR",
SendType: descriptor.SendTypeCyclic,
CycleTime: 100 * time.Millisecond,
Signals: []*descriptor.Signal{
{
Name: "Mux",
IsMultiplexer: true,
Start: 0,
Length: 4,
Scale: 1,
ReceiverNodes: []string{"DRIVER", "IO"},
},
{
Name: "ErrCount",
Start: 4,
Length: 12,
Scale: 1,
ReceiverNodes: []string{"DRIVER", "IO"},
},
{
Name: "Left",
IsMultiplexed: true,
MultiplexerValue: 0,
Start: 16,
Length: 12,
Scale: 0.1,
ReceiverNodes: []string{"DRIVER", "IO"},
},
{
Name: "NoFiltLeft",
IsMultiplexed: true,
MultiplexerValue: 1,
Start: 16,
Length: 12,
Scale: 0.1,
ReceiverNodes: []string{"DBG"},
},
{
Name: "Middle",
IsMultiplexed: true,
MultiplexerValue: 0,
Start: 28,
Length: 12,
Scale: 0.1,
ReceiverNodes: []string{"DRIVER", "IO"},
},
{
Name: "NoFiltMiddle",
IsMultiplexed: true,
MultiplexerValue: 1,
Start: 28,
Length: 12,
Scale: 0.1,
ReceiverNodes: []string{"DBG"},
},
{
Name: "Right",
IsMultiplexed: true,
MultiplexerValue: 0,
Start: 40,
Length: 12,
Scale: 0.1,
ReceiverNodes: []string{"DRIVER", "IO"},
},
{
Name: "NoFiltRight",
IsMultiplexed: true,
MultiplexerValue: 1,
Start: 40,
Length: 12,
Scale: 0.1,
ReceiverNodes: []string{"DBG"},
},
{
Name: "Rear",
IsMultiplexed: true,
MultiplexerValue: 0,
Start: 52,
Length: 12,
Scale: 0.1,
ReceiverNodes: []string{"DRIVER", "IO"},
},
{
Name: "NoFiltRear",
IsMultiplexed: true,
MultiplexerValue: 1,
Start: 52,
Length: 12,
Scale: 0.1,
ReceiverNodes: []string{"DBG"},
},
},
}
exampleDatabase.Messages[400] = &descriptor.Message{
ID: 400,
Name: "MotorStatus",
Length: 3,
SenderNode: "MOTOR",
SendType: descriptor.SendTypeCyclic,
CycleTime: 100 * time.Millisecond,
Signals: []*descriptor.Signal{
{
Name: "WheelError",
Start: 0,
Length: 1,
Scale: 1,
ReceiverNodes: []string{"DRIVER", "IO"},
},
{
Name: "SpeedKph",
Start: 8,
Length: 16,
Scale: 0.001,
Unit: "km/h",
ReceiverNodes: []string{"DRIVER", "IO"},
},
},
}
exampleDatabase.Messages[500] = &descriptor.Message{
ID: 500,
Name: "IODebug",
Length: 6,
SenderNode: "IO",
SendType: descriptor.SendTypeEvent,
Signals: []*descriptor.Signal{
{
Name: "TestUnsigned",
Start: 0,
Length: 8,
Scale: 1,
ReceiverNodes: []string{"DBG"},
},
{
Name: "TestEnum",
Start: 8,
Length: 6,
Scale: 1,
ReceiverNodes: []string{"DBG"},
DefaultValue: int(examplecan.IODebug_TestEnum_Two),
ValueDescriptions: []*descriptor.ValueDescription{
{Value: 1, Description: "One"},
{Value: 2, Description: "Two"},
},
},
{
Name: "TestSigned",
Start: 16,
Length: 8,
IsSigned: true,
Scale: 1,
ReceiverNodes: []string{"DBG"},
},
{
Name: "TestFloat",
Start: 24,
Length: 8,
Scale: 0.5,
ReceiverNodes: []string{"DBG"},
},
{
Name: "TestBoolEnum",
Start: 32,
Length: 1,
Scale: 1,
ReceiverNodes: []string{"DBG"},
ValueDescriptions: []*descriptor.ValueDescription{
{Value: 0, Description: "Zero"},
{Value: 1, Description: "One"},
},
},
{
Name: "TestScaledEnum",
Start: 40,
Length: 2,
Scale: 2,
Min: 0,
Max: 6,
ReceiverNodes: []string{"DBG"},
ValueDescriptions: []*descriptor.ValueDescription{
{Value: 0, Description: "Zero"},
{Value: 1, Description: "Two"},
{Value: 2, Description: "Four"},
{Value: 3, Description: "Six"},
},
},
},
}
input, err := ioutil.ReadFile(exampleDBCFile)
assert.NilError(t, err)
result, err := Compile(exampleDBCFile, input)
if err != nil {
t.Fatal(err)
}
if len(result.Warnings) > 0 {
t.Fatal(result.Warnings)
}
assert.DeepEqual(t, exampleDatabase, result.Database)
}

View File

@@ -0,0 +1,338 @@
package generate
import (
"context"
"fmt"
"net"
"reflect"
"testing"
"time"
can "github.com/fiskerinc/cloud-services/pkg/can-go"
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/generated"
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/socketcan"
examplecan "github.com/fiskerinc/cloud-services/pkg/can-go/testdata/gen/go/example"
"golang.org/x/sync/errgroup"
"gotest.tools/v3/assert"
)
func TestExampleDatabase_MarshalUnmarshal(t *testing.T) {
for _, tt := range []struct {
name string
m can.Message
f can.Frame
}{
{
name: "IODebug",
m: examplecan.NewIODebug().
SetTestUnsigned(5).
SetTestEnum(examplecan.IODebug_TestEnum_Two).
SetTestSigned(-42).
SetTestFloat(61.5).
SetTestBoolEnum(examplecan.IODebug_TestBoolEnum_One).
SetRawTestScaledEnum(examplecan.IODebug_TestScaledEnum_Four),
f: can.Frame{
ID: 500,
Length: 6,
Data: can.Data{5, 2, 214, 123, 1, 2},
},
},
{
name: "MotorStatus1",
m: examplecan.NewMotorStatus().
SetSpeedKph(0.423).
SetWheelError(true),
f: can.Frame{
ID: 400,
Length: 3,
Data: can.Data{0x1, 0xa7, 0x1},
},
},
{
name: "MotorStatus2",
m: examplecan.NewMotorStatus().
SetSpeedKph(12),
f: can.Frame{
ID: 400,
Length: 3,
Data: can.Data{0x00, 0xe0, 0x2e},
},
},
} {
tt := tt
t.Run(tt.name, func(t *testing.T) {
f, err := tt.m.MarshalFrame()
assert.NilError(t, err)
assert.Equal(t, tt.f, f)
// allocate new message of same type as tt.m
msg := reflect.New(reflect.ValueOf(tt.m).Elem().Type()).Interface().(generated.Message)
assert.NilError(t, msg.UnmarshalFrame(f))
assert.Assert(t, reflect.DeepEqual(tt.m, msg))
})
}
}
func TestExampleDatabase_UnmarshalFrame_Error(t *testing.T) {
for _, tt := range []struct {
name string
f can.Frame
m generated.Message
err string
}{
{
name: "wrong ID",
f: can.Frame{ID: 11, Length: 8},
m: examplecan.NewSensorSonars(),
err: "unmarshal SensorSonars: expects ID 200 (got 00B#0000000000000000 with ID 11)",
},
{
name: "wrong length",
f: can.Frame{ID: 200, Length: 4},
m: examplecan.NewSensorSonars(),
err: "unmarshal SensorSonars: expects length 8 (got 0C8#00000000 with length 4)",
},
{
name: "remote frame",
f: can.Frame{ID: 200, Length: 8, IsRemote: true},
m: examplecan.NewSensorSonars(),
err: "unmarshal SensorSonars: expects non-remote frame (got remote frame 0C8#R8)",
},
{
name: "extended ID",
f: can.Frame{ID: 200, Length: 8, IsExtended: true},
m: examplecan.NewSensorSonars(),
err: "unmarshal SensorSonars: expects standard ID (got 000000C8#0000000000000000 with extended ID)",
},
} {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.err, tt.m.UnmarshalFrame(tt.f).Error())
})
}
}
func TestExampleDatabase_TestEnum_String(t *testing.T) {
assert.Equal(t, "One", examplecan.IODebug_TestEnum_One.String())
assert.Equal(t, "Two", examplecan.IODebug_TestEnum_Two.String())
assert.Equal(t, "IODebug_TestEnum(3)", examplecan.IODebug_TestEnum(3).String())
}
func TestExampleDatabase_Message_String(t *testing.T) {
const expected = "{WheelError: true, SpeedKph: 42km/h}"
msg := examplecan.NewMotorStatus().
SetSpeedKph(42).
SetWheelError(true)
assert.Equal(t, expected, msg.String())
assert.Equal(t, expected, fmt.Sprintf("%v", msg))
}
func TestExampleDatabase_OutOfBoundsValue(t *testing.T) {
const expected = examplecan.IODebug_TestEnum(63)
actual := examplecan.NewIODebug().SetTestEnum(255).TestEnum()
assert.Equal(t, expected, actual)
}
func TestExampleDatabase_MultiplexedSignals(t *testing.T) {
// Given a message with multiplexed signals
msg := examplecan.NewSensorSonars().
SetErrCount(1).
SetMux(1).
SetLeft(20).
SetMiddle(30).
SetRight(40).
SetRear(50).
SetNoFiltLeft(60).
SetNoFiltMiddle(70).
SetNoFiltRight(80).
SetNoFiltRear(90)
for _, tt := range []struct {
expectedMux uint8
expectedErrCount uint16
expectedLeft float64
expectedMiddle float64
expectedRight float64
expectedRear float64
expectedNoFiltLeft float64
expectedNoFiltMiddle float64
expectedNoFiltRight float64
expectedNoFiltRear float64
}{
{
expectedMux: 0,
expectedErrCount: 1,
expectedLeft: 20,
expectedMiddle: 30,
expectedRight: 40,
expectedRear: 50,
expectedNoFiltLeft: 0,
expectedNoFiltMiddle: 0,
expectedNoFiltRight: 0,
expectedNoFiltRear: 0,
},
{
expectedMux: 1,
expectedErrCount: 1,
expectedLeft: 0,
expectedMiddle: 0,
expectedRight: 0,
expectedRear: 0,
expectedNoFiltLeft: 60,
expectedNoFiltMiddle: 70,
expectedNoFiltRight: 80,
expectedNoFiltRear: 90,
},
} {
tt := tt
t.Run(fmt.Sprintf("mux=%v", tt.expectedMux), func(t *testing.T) {
unmarshal1 := examplecan.NewSensorSonars()
// When the multiplexer signal is 0 and we marshal the message
// to a CAN frame
msg.SetMux(tt.expectedMux)
f1, err := msg.MarshalFrame()
assert.NilError(t, err)
// When we unmarshal the CAN frame back to a message
assert.NilError(t, unmarshal1.UnmarshalFrame(f1))
// Then only the multiplexed signals with multiplexer value 0
// should be unmarshaled
assert.Equal(t, tt.expectedMux, unmarshal1.Mux(), "Mux")
assert.Equal(t, tt.expectedErrCount, unmarshal1.ErrCount(), "ErrCount")
assert.Equal(t, tt.expectedLeft, unmarshal1.Left(), "Left")
assert.Equal(t, tt.expectedMiddle, unmarshal1.Middle(), "Middle")
assert.Equal(t, tt.expectedRight, unmarshal1.Right(), "Right")
assert.Equal(t, tt.expectedRear, unmarshal1.Rear(), "Rear")
assert.Equal(t, tt.expectedNoFiltLeft, unmarshal1.NoFiltLeft(), "NoFiltLeft")
assert.Equal(t, tt.expectedNoFiltMiddle, unmarshal1.NoFiltMiddle(), "NoFiltMiddle")
assert.Equal(t, tt.expectedNoFiltRight, unmarshal1.NoFiltRight(), "NoFiltRight")
assert.Equal(t, tt.expectedNoFiltRear, unmarshal1.NoFiltRear(), "NoFiltRear")
})
}
}
func TestExampleDatabase_CopyFrom(t *testing.T) {
// Given: an original message
from := examplecan.NewIODebug().
SetRawTestScaledEnum(examplecan.IODebug_TestScaledEnum_Four).
SetTestBoolEnum(true).
SetTestFloat(0.1).
SetTestSigned(-10).
SetTestUnsigned(10)
// When: another message copies from the original message
to := examplecan.NewIODebug().CopyFrom(from)
// Then:
// all fields should be equal...
assert.Equal(t, from.String(), to.String())
assert.Equal(t, from.TestScaledEnum(), to.TestScaledEnum())
assert.Equal(t, from.TestBoolEnum(), to.TestBoolEnum())
assert.Equal(t, from.TestFloat(), to.TestFloat())
assert.Equal(t, from.TestSigned(), to.TestSigned())
assert.Equal(t, from.TestUnsigned(), to.TestUnsigned())
// ...and changes to the original should not affect the new message
from.SetTestUnsigned(100)
assert.Equal(t, uint8(10), to.TestUnsigned())
}
func TestExample_Nodes(t *testing.T) {
const testTimeout = 2 * time.Second
requireVCAN0(t)
// given a DRIVER node and a MOTOR node
motor := examplecan.NewMOTOR("can", "vcan0")
driver := examplecan.NewDRIVER("can", "vcan0")
// when starting them
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
return motor.Run(ctx)
})
g.Go(func() error {
return driver.Run(ctx)
})
// and the MOTOR node is configured to send a speed report
const expectedSpeedKph = 42
motor.Lock()
motor.Tx().MotorStatus().SetSpeedKph(expectedSpeedKph)
motor.Tx().MotorStatus().SetCyclicTransmissionEnabled(true)
motor.Unlock()
// and the DRIVER node is configured to send a steering command
const expectedSteer = -4
driver.Lock()
driver.Tx().MotorCommand().SetSteer(expectedSteer)
driver.Tx().MotorCommand().SetCyclicTransmissionEnabled(true)
driver.Unlock()
// and the MOTOR node is listening for the steering command
expectedSteerReceivedChan := make(chan struct{})
motor.Lock()
motor.Rx().MotorCommand().SetAfterReceiveHook(func(context.Context) error {
motor.Lock()
if motor.Rx().MotorCommand().Steer() == expectedSteer {
close(expectedSteerReceivedChan)
motor.Rx().MotorCommand().SetAfterReceiveHook(func(context.Context) error { return nil })
}
motor.Unlock()
return nil
})
motor.Unlock()
// and the DRIVER node is listening for the speed report
expectedSpeedReceivedChan := make(chan struct{})
driver.Lock()
driver.Rx().MotorStatus().SetAfterReceiveHook(func(context.Context) error {
driver.Lock()
if driver.Rx().MotorStatus().SpeedKph() == expectedSpeedKph {
close(expectedSpeedReceivedChan)
driver.Rx().MotorStatus().SetAfterReceiveHook(func(context.Context) error { return nil })
}
driver.Unlock()
return nil
})
driver.Unlock()
// then the steer command transmitted by DRIVER should be received by MOTOR
select {
case <-expectedSteerReceivedChan:
case <-ctx.Done():
t.Fatalf("expected steer not received: %v", expectedSteer)
}
// and the speed report transmitted by MOTOR should be received by DRIVER
select {
case <-expectedSpeedReceivedChan:
case <-ctx.Done():
t.Fatalf("expected speed not received: %v", expectedSpeedKph)
}
cancel()
assert.NilError(t, g.Wait())
}
func TestExample_Node_NoEmptyMessages(t *testing.T) {
const testTimeout = 2 * time.Second
requireVCAN0(t)
// given a DRIVER node and a MOTOR node
motor := examplecan.NewMOTOR("can", "vcan0")
// when starting them
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
handler := func(ctx context.Context) error {
motor.Lock()
motor.Tx().MotorStatus().SetSpeedKph(100).SetWheelError(true)
motor.Unlock()
return nil
}
motor.Tx().MotorStatus().SetBeforeTransmitHook(handler)
motor.Tx().MotorStatus().SetCyclicTransmissionEnabled(true)
c, err := socketcan.Dial("can", "vcan0")
r := socketcan.NewReceiver(c)
assert.NilError(t, err)
g := errgroup.Group{}
g.Go(func() error {
return motor.Run(ctx)
})
assert.Assert(t, r.Receive())
assert.Equal(t, examplecan.NewMotorStatus().SetSpeedKph(100).SetWheelError(true).Frame(), r.Frame())
cancel()
assert.NilError(t, g.Wait())
}
func requireVCAN0(t *testing.T) {
t.Helper()
if _, err := net.InterfaceByName("vcan0"); err != nil {
t.Skip("interface vcan0 does not exist")
}
}

View File

@@ -0,0 +1,976 @@
package generate
import (
"bytes"
"fmt"
"go/format"
"go/types"
"path"
"strings"
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor"
"github.com/shurcooL/go-goon"
)
type File struct {
buf bytes.Buffer
err error
}
func NewFile() *File {
f := &File{}
f.buf.Grow(1e5) // 100K
return f
}
func (f *File) Write(p []byte) (int, error) {
if f.err != nil {
return 0, f.err
}
n, err := f.buf.Write(p)
f.err = err
return n, err
}
func (f *File) P(v ...interface{}) {
for _, x := range v {
_, _ = fmt.Fprint(f, x)
}
_, _ = fmt.Fprintln(f)
}
func (f *File) Dump(v interface{}) {
_, _ = goon.Fdump(f, v)
}
func (f *File) Content() ([]byte, error) {
if f.err != nil {
return nil, fmt.Errorf("file content: %w", f.err)
}
formatted, err := format.Source(f.buf.Bytes())
if err != nil {
return nil, fmt.Errorf("file content: %s: %w", f.buf.String(), err)
}
return formatted, nil
}
func Database(h string, d *descriptor.Database) ([]byte, error) {
f := NewFile()
Package(f, d)
Imports(f)
Version(f, h, d.Version)
ListECUs(f, d)
for _, m := range d.Messages {
if m == nil {
continue
}
MessageType(f, m)
for _, s := range m.Signals {
if hasCustomType(s) {
SignalCustomType(f, m, s)
}
}
MarshalFrame(f, m)
UnmarshalFrame(f, m)
}
if hasSendType(d) { // only code-generate nodes for schemas with send types specified
for _, n := range d.Nodes {
Node(f, d, n)
}
}
Descriptors(f, d)
return f.Content()
}
func Package(f *File, d *descriptor.Database) {
packageName := strings.TrimSuffix(path.Base(d.SourceFile), path.Ext(d.SourceFile)) + "can"
f.P("// Package ", packageName, " provides primitives for encoding and decoding ", d.Name(), " CAN messages.")
f.P("//")
f.P("// Source: ", d.SourceFile)
f.P("package ", packageName)
f.P()
}
func Imports(f *File) {
f.P("import (")
f.P(`"context"`)
f.P(`"fmt"`)
f.P(`"net"`)
f.P(`"net/http"`)
f.P(`"sync"`)
f.P(`"time"`)
f.P()
f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go"`)
f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/socketcan"`)
f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/candebug"`)
f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/canrunner"`)
f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor"`)
f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/generated"`)
f.P(`"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/cantext"`)
f.P(")")
f.P()
// we could use goimports for this, but it significantly slows down code generation
f.P("// prevent unused imports")
f.P("var (")
f.P("_ = context.Background")
f.P("_ = fmt.Print")
f.P("_ = net.Dial")
f.P("_ = http.Error")
f.P("_ = sync.Mutex{}")
f.P("_ = time.Now")
f.P("_ = socketcan.Dial")
f.P("_ = candebug.ServeMessagesHTTP")
f.P("_ = canrunner.Run")
f.P(")")
f.P()
f.P("// Generated code. DO NOT EDIT.")
}
func Version(f *File, h string, v string) {
f.P()
f.P("// Hash used as versioning control for DBC")
f.P(`const Hash string = "`, h, `"`)
f.P("// Version is the version listed in the DBC")
f.P(`const Version string = "`, v, `"`)
f.P()
}
func ListECUs(f *File, d *descriptor.Database) {
f.P()
f.P("// ECUs parsed from DBC")
ecuList := "var ECUs = []string{"
i := 0
for ecu := range d.ECUs {
ecuList += fmt.Sprintf(`"%s"`, ecu)
i++
if i != len(d.ECUs) {
ecuList += ", "
}
}
ecuList += "}"
f.P(ecuList)
f.P()
}
func SignalCustomType(f *File, m *descriptor.Message, s *descriptor.Signal) {
f.P("// ", signalType(m, s), " models the ", s.Name, " signal of the ", m.Name, " message.")
f.P("type ", signalType(m, s), " ", signalPrimitiveType(s))
f.P()
// dtaylor@fiskerinc.com EDIT
// f.P("// Value descriptions for the ", s.Name, " signal of the ", m.Name, " message.")
// f.P("const (")
// for _, vd := range s.ValueDescriptions {
// switch {
// case s.Length == 1 && vd.Value == 1:
// f.P(signalType(m, s), "_", vd.Description, " ", signalType(m, s), " = true")
// case s.Length == 1 && vd.Value == 0:
// f.P(signalType(m, s), "_", vd.Description, " ", signalType(m, s), " = false")
// default:
// f.P(signalType(m, s), "_", vd.Description, " ", signalType(m, s), " = ", vd.Value)
// }
// }
// f.P(")")
// f.P()
f.P("func (v ", signalType(m, s), ") String() string {")
if s.Length == 1 {
f.P("switch bool(v) {")
for _, vd := range s.ValueDescriptions {
if vd.Value == 1 {
f.P("case true:")
} else {
f.P("case false:")
}
f.P(`return "`, vd.Description, `"`)
}
f.P("}")
f.P(`return fmt.Sprintf("`, signalType(m, s), `(%t)", v)`)
} else {
f.P("switch v {")
for _, vd := range s.ValueDescriptions {
f.P("case ", vd.Value, ":")
f.P(`return "`, vd.Description, `"`)
}
f.P("default:")
f.P(`return fmt.Sprintf("`, signalType(m, s), `(%d)", v)`)
f.P("}")
}
f.P("}")
}
func MessageType(f *File, m *descriptor.Message) {
f.P("// ", messageReaderInterface(m), " provides read access to a ", m.Name, " message.")
f.P("type ", messageReaderInterface(m), " interface {")
for _, s := range m.Signals {
if hasPhysicalRepresentation(s) {
f.P("// ", s.Name, " returns the physical value of the ", s.Name, " signal.")
f.P(s.Name, "() float64")
if len(s.ValueDescriptions) > 0 {
f.P()
f.P("// ", s.Name, " returns the raw (encoded) value of the ", s.Name, " signal.")
f.P("Raw", s.Name, "() ", signalType(m, s))
}
} else {
f.P("// ", s.Name, " returns the value of the ", s.Name, " signal.")
f.P(s.Name, "()", signalType(m, s))
}
}
f.P("}")
f.P()
f.P("// ", messageWriterInterface(m), " provides write access to a ", m.Name, " message.")
f.P("type ", messageWriterInterface(m), " interface {")
f.P("// CopyFrom copies all values from ", messageReaderInterface(m), ".")
f.P("CopyFrom(", messageReaderInterface(m), ") *", messageStruct(m))
for _, s := range m.Signals {
if hasPhysicalRepresentation(s) {
f.P("// Set", s.Name, " sets the physical value of the ", s.Name, " signal.")
f.P("Set", s.Name, "(float64) *", messageStruct(m))
if len(s.ValueDescriptions) > 0 {
f.P()
f.P("// SetRaw", s.Name, " sets the raw (encoded) value of the ", s.Name, " signal.")
f.P("SetRaw", s.Name, "(", signalType(m, s), ") *", messageStruct(m))
}
} else {
f.P("// Set", s.Name, " sets the value of the ", s.Name, " signal.")
f.P("Set", s.Name, "(", signalType(m, s), ") *", messageStruct(m))
}
}
f.P("}")
f.P()
f.P("type ", messageStruct(m), " struct {")
for _, s := range m.Signals {
f.P(signalField(s), " ", signalType(m, s))
}
f.P("}")
f.P()
f.P("func New", messageStruct(m), "() *", messageStruct(m), " {")
f.P("m := &", messageStruct(m), "{}")
f.P("m.Reset()")
f.P("return m")
f.P("}")
f.P()
f.P("func (m *", messageStruct(m), ") Reset() {")
for _, s := range m.Signals {
switch {
case s.Length == 1 && s.DefaultValue == 1:
f.P("m.", signalField(s), " = true")
case s.Length == 1:
f.P("m.", signalField(s), " = false")
default:
f.P("m.", signalField(s), " = ", s.DefaultValue)
}
}
f.P("}")
f.P()
f.P("func (m *", messageStruct(m), ") CopyFrom(o ", messageReaderInterface(m), ") *", messageStruct(m), "{")
for _, s := range m.Signals {
if hasPhysicalRepresentation(s) {
f.P("m.Set", s.Name, "(o.", s.Name, "())")
} else {
f.P("m.", signalField(s), " = o.", s.Name, "()")
}
}
f.P("return m")
f.P("}")
f.P()
f.P("// Descriptor returns the ", m.Name, " descriptor.")
f.P("func (m *", messageStruct(m), ") Descriptor() *descriptor.Message {")
f.P("return ", messageDescriptor(m), ".Message")
f.P("}")
f.P()
f.P("// String returns a compact string representation of the message.")
f.P("func(m *", messageStruct(m), ") String() string {")
f.P("return cantext.MessageString(m)")
f.P("}")
f.P()
for _, s := range m.Signals {
if !hasPhysicalRepresentation(s) {
f.P("func (m *", messageStruct(m), ") ", s.Name, "() ", signalType(m, s), " {")
f.P("return m.", signalField(s))
f.P("}")
f.P()
f.P("func (m *", messageStruct(m), ") Set", s.Name, "(v ", signalType(m, s), ") *", messageStruct(m), " {")
if s.Length == 1 {
f.P("m.", signalField(s), " = v")
} else {
f.P(
"m.", signalField(s), " = ", signalType(m, s), "(",
signalDescriptor(m, s), ".SaturatedCast", signalSuperType(s), "(",
signalPrimitiveSuperType(s), "(v)))",
)
}
f.P("return m")
f.P("}")
f.P()
continue
}
f.P("func (m *", messageStruct(m), ") ", s.Name, "() float64 {")
f.P("return ", signalDescriptor(m, s), ".ToPhysical(float64(m.", signalField(s), "))")
f.P("}")
f.P()
f.P("func (m *", messageStruct(m), ") Set", s.Name, "(v float64) *", messageStruct(m), " {")
f.P("m.", signalField(s), " = ", signalType(m, s), "(", signalDescriptor(m, s), ".FromPhysical(v))")
f.P("return m")
f.P("}")
f.P()
if len(s.ValueDescriptions) > 0 {
f.P("func (m *", messageStruct(m), ") Raw", s.Name, "() ", signalType(m, s), " {")
f.P("return m.", signalField(s))
f.P("}")
f.P()
f.P("func (m *", messageStruct(m), ") SetRaw", s.Name, "(v ", signalType(m, s), ") *", messageStruct(m), "{")
f.P(
"m.", signalField(s), " = ", signalType(m, s), "(",
signalDescriptor(m, s), ".SaturatedCast", signalSuperType(s), "(",
signalPrimitiveSuperType(s), "(v)))",
)
f.P("return m")
f.P("}")
f.P()
}
}
}
func Descriptors(f *File, d *descriptor.Database) {
f.P("// Nodes returns the ", d.Name(), " node descriptors.")
f.P("func Nodes() *NodesDescriptor {")
f.P("return nd")
f.P("}")
f.P()
f.P("// NodesDescriptor contains all ", d.Name(), " node descriptors.")
f.P("type NodesDescriptor struct{")
for _, n := range d.Nodes {
f.P(n.Name, " *descriptor.Node")
}
f.P("}")
f.P()
f.P("// Messages returns the ", d.Name(), " message descriptors.")
f.P("func Messages() *MessagesDescriptor {")
f.P("return md")
f.P("}")
f.P()
f.P("// MessagesDescriptor contains all ", d.Name(), " message descriptors.")
f.P("type MessagesDescriptor struct{")
for _, m := range d.Messages {
if m == nil {
continue
}
f.P(m.Name, " *", m.Name, "Descriptor")
}
f.P("}")
f.P()
f.P("// UnmarshalFrame unmarshals the provided ", d.Name(), " CAN frame.")
f.P("func (md *MessagesDescriptor) UnmarshalFrame(f can.Frame) (generated.Message, error) {")
f.P("switch f.ID {")
for _, m := range d.Messages {
if m == nil {
continue
}
f.P("case md.", m.Name, ".ID:")
f.P("var msg ", messageStruct(m))
f.P("if err := msg.UnmarshalFrame(f); err != nil {")
f.P(`return nil, fmt.Errorf("unmarshal `, d.Name(), ` frame: %w", err)`)
f.P("}")
f.P("return &msg, nil")
}
f.P("default:")
f.P(`return nil, fmt.Errorf("unmarshal `, d.Name(), ` frame: ID not in database: %d", f.ID)`)
f.P("}")
f.P("}")
f.P()
for _, m := range d.Messages {
if m == nil {
continue
}
f.P("type ", m.Name, "Descriptor struct{")
f.P("*descriptor.Message")
for _, s := range m.Signals {
f.P(s.Name, " *descriptor.Signal")
}
f.P("}")
f.P()
}
f.P("// Database returns the ", d.Name(), " database descriptor.")
f.P("func (md *MessagesDescriptor) Database() *descriptor.Database {")
f.P("return d")
f.P("}")
f.P()
f.P("var nd = &NodesDescriptor{")
for ni, n := range d.Nodes {
f.P(n.Name, ": d.Nodes[", ni, "],")
}
f.P("}")
f.P()
f.P("var md = &MessagesDescriptor{")
for mi, m := range d.Messages {
if m == nil {
continue
}
f.P(m.Name, ": &", m.Name, "Descriptor{")
f.P("Message: d.Messages[", mi, "],")
for si, s := range m.Signals {
f.P(s.Name, ": d.Messages[", mi, "].Signals[", si, "],")
}
f.P("},")
}
f.P("}")
f.P()
f.P("var d = ")
f.Dump(d)
f.P()
}
func MarshalFrame(f *File, m *descriptor.Message) {
f.P("// Frame returns a CAN frame representing the message.")
f.P("func (m *", messageStruct(m), ") Frame() can.Frame {")
f.P("md := ", messageDescriptor(m))
f.P("f := can.Frame{ID: md.ID, IsExtended: md.IsExtended, Length: md.Length}")
for _, s := range m.Signals {
if s.IsMultiplexed {
continue
}
f.P(
"md.", s.Name, ".Marshal", signalSuperType(s),
"(&f.Data, ", signalPrimitiveSuperType(s), "(m.", signalField(s), "))",
)
}
if mux, ok := m.MultiplexerSignal(); ok {
for _, s := range m.Signals {
if !s.IsMultiplexed {
continue
}
f.P("if m.", signalField(mux), " == ", s.MultiplexerValue, " {")
f.P(
"md.", s.Name, ".Marshal", signalSuperType(s), "(&f.Data, ", signalPrimitiveSuperType(s),
"(m.", signalField(s), "))",
)
f.P("}")
}
}
f.P("return f")
f.P("}")
f.P()
f.P("// MarshalFrame encodes the message as a CAN frame.")
f.P("func (m *", messageStruct(m), ") MarshalFrame() (can.Frame, error) {")
f.P("return m.Frame(), nil")
f.P("}")
f.P()
}
func UnmarshalFrame(f *File, m *descriptor.Message) {
f.P("// UnmarshalFrame decodes the message from a CAN frame.")
f.P("func (m *", messageStruct(m), ") UnmarshalFrame(f can.Frame) error {")
f.P("md := ", messageDescriptor(m))
// generate frame checks
id := func(isExtended bool) string {
if isExtended {
return "extended ID"
}
return "standard ID"
}
f.P("switch {")
f.P("case f.ID != md.ID:")
f.P(`return fmt.Errorf(`)
f.P(`"unmarshal `, m.Name, `: expects ID `, m.ID, ` (got %s with ID %d)", f.String(), f.ID,`)
f.P(`)`)
f.P("case f.Length != md.Length:")
f.P(`return fmt.Errorf(`)
f.P(`"unmarshal `, m.Name, `: expects length `, m.Length, ` (got %s with length %d)", f.String(), f.Length,`)
f.P(`)`)
f.P("case f.IsRemote:")
f.P(`return fmt.Errorf(`)
f.P(`"unmarshal `, m.Name, `: expects non-remote frame (got remote frame %s)", f.String(),`)
f.P(`)`)
f.P("case f.IsExtended != md.IsExtended:")
f.P(`return fmt.Errorf(`)
f.P(`"unmarshal `, m.Name, `: expects `, id(m.IsExtended), ` (got %s with `, id(!m.IsExtended), `)", f.String(),`)
f.P(`)`)
f.P("}")
if len(m.Signals) == 0 {
f.P("return nil")
f.P("}")
return
}
// generate non-multiplexed signal unmarshaling
for _, s := range m.Signals {
if s.IsMultiplexed {
continue
}
f.P("m.", signalField(s), " = ", signalType(m, s), "(md.", s.Name, ".Unmarshal", signalSuperType(s), "(f.Data))")
}
// generate multiplexed signal unmarshaling
if mux, ok := m.MultiplexerSignal(); ok {
for _, s := range m.Signals {
if !s.IsMultiplexed {
continue
}
f.P("if m.", signalField(mux), " == ", s.MultiplexerValue, " {")
f.P("m.", signalField(s), " = ", signalType(m, s), "(md.", s.Name, ".Unmarshal", signalSuperType(s), "(f.Data))")
f.P("}")
}
}
f.P("return nil")
f.P("}")
f.P()
}
func Node(f *File, d *descriptor.Database, n *descriptor.Node) {
rxMessages := collectRxMessages(d, n)
txMessages := collectTxMessages(d, n)
f.P("type ", nodeInterface(n), " interface {")
f.P("sync.Locker")
f.P("Tx() ", txGroupInterface(n))
f.P("Rx() ", rxGroupInterface(n))
f.P("Run(ctx context.Context) error")
f.P("}")
f.P()
f.P("type ", rxGroupInterface(n), " interface {")
f.P("http.Handler // for debugging")
for _, m := range rxMessages {
f.P(m.Name, "() ", rxMessageInterface(n, m))
}
f.P("}")
f.P()
f.P("type ", txGroupInterface(n), " interface {")
f.P("http.Handler // for debugging")
for _, m := range txMessages {
f.P(m.Name, "() ", txMessageInterface(n, m))
}
f.P("}")
f.P()
for _, m := range rxMessages {
f.P("type ", rxMessageInterface(n, m), " interface {")
f.P(messageReaderInterface(m))
f.P("ReceiveTime() time.Time")
f.P("SetAfterReceiveHook(h func(context.Context) error)")
f.P("}")
f.P()
}
for _, m := range txMessages {
f.P("type ", txMessageInterface(n, m), " interface {")
f.P(messageReaderInterface(m))
f.P(messageWriterInterface(m))
f.P("TransmitTime() time.Time")
f.P("Transmit(ctx context.Context) error")
f.P("SetBeforeTransmitHook(h func(context.Context) error)")
if m.SendType == descriptor.SendTypeCyclic {
f.P("// SetCyclicTransmissionEnabled enables/disables cyclic transmission.")
f.P("SetCyclicTransmissionEnabled(bool)")
f.P("// IsCyclicTransmissionEnabled returns whether cyclic transmission is enabled/disabled.")
f.P("IsCyclicTransmissionEnabled() bool")
}
f.P("}")
f.P()
}
f.P("type ", nodeStruct(n), " struct {")
f.P("sync.Mutex // protects all node state")
f.P("network string")
f.P("address string")
f.P("rx ", rxGroupStruct(n))
f.P("tx ", txGroupStruct(n))
f.P("}")
f.P()
f.P("var _ ", nodeInterface(n), " = &", nodeStruct(n), "{}")
f.P("var _ canrunner.Node = &", nodeStruct(n), "{}")
f.P()
f.P("func New", nodeInterface(n), "(network, address string) ", nodeInterface(n), " {")
f.P("n := &", nodeStruct(n), "{network: network, address: address}")
f.P("n.rx.parentMutex = &n.Mutex")
f.P("n.tx.parentMutex = &n.Mutex")
for _, m := range rxMessages {
f.P("n.rx.", messageField(m), ".init()")
f.P("n.rx.", messageField(m), ".Reset()")
}
for _, m := range txMessages {
f.P("n.tx.", messageField(m), ".init()")
f.P("n.tx.", messageField(m), ".Reset()")
}
f.P("return n")
f.P("}")
f.P()
f.P("func (n *", nodeStruct(n), ") Run(ctx context.Context) error {")
f.P("return canrunner.Run(ctx, n)")
f.P("}")
f.P()
f.P("func (n *", nodeStruct(n), ") Rx() ", rxGroupInterface(n), " {")
f.P("return &n.rx")
f.P("}")
f.P()
f.P("func (n *", nodeStruct(n), ") Tx() ", txGroupInterface(n), " {")
f.P("return &n.tx")
f.P("}")
f.P()
f.P("type ", rxGroupStruct(n), " struct {")
f.P("parentMutex *sync.Mutex")
for _, m := range rxMessages {
f.P(messageField(m), " ", rxMessageStruct(n, m))
}
f.P("}")
f.P()
f.P("var _ ", rxGroupInterface(n), " = &", rxGroupStruct(n), "{}")
f.P()
f.P("func (rx *", rxGroupStruct(n), ") ServeHTTP(w http.ResponseWriter, r *http.Request) {")
f.P("rx.parentMutex.Lock()")
f.P("defer rx.parentMutex.Unlock()")
f.P("candebug.ServeMessagesHTTP(w, r, []generated.Message{")
for _, m := range rxMessages {
f.P("&rx.", messageField(m), ",")
}
f.P("})")
f.P("}")
f.P()
for _, m := range rxMessages {
f.P("func (rx *", rxGroupStruct(n), ") ", m.Name, "() ", rxMessageInterface(n, m), " {")
f.P("return &rx.", messageField(m))
f.P("}")
f.P()
}
f.P()
f.P("type ", txGroupStruct(n), " struct {")
f.P("parentMutex *sync.Mutex")
for _, m := range txMessages {
f.P(messageField(m), " ", txMessageStruct(n, m))
}
f.P("}")
f.P()
f.P("var _ ", txGroupInterface(n), " = &", txGroupStruct(n), "{}")
f.P()
f.P("func (tx *", txGroupStruct(n), ") ServeHTTP(w http.ResponseWriter, r *http.Request) {")
f.P("tx.parentMutex.Lock()")
f.P("defer tx.parentMutex.Unlock()")
f.P("candebug.ServeMessagesHTTP(w, r, []generated.Message{")
for _, m := range txMessages {
f.P("&tx.", messageField(m), ",")
}
f.P("})")
f.P("}")
f.P()
for _, m := range txMessages {
f.P("func (tx *", txGroupStruct(n), ") ", m.Name, "() ", txMessageInterface(n, m), " {")
f.P("return &tx.", messageField(m))
f.P("}")
f.P()
}
f.P()
f.P("func (n *", nodeStruct(n), ") Descriptor() *descriptor.Node {")
f.P("return ", nodeDescriptor(n))
f.P("}")
f.P()
f.P("func (n *", nodeStruct(n), ") Connect() (net.Conn, error) {")
f.P("return socketcan.Dial(n.network, n.address)")
f.P("}")
f.P()
f.P("func (n *", nodeStruct(n), ") ReceivedMessage(id uint32) (canrunner.ReceivedMessage, bool) {")
f.P("switch id {")
for _, m := range rxMessages {
f.P("case ", m.ID, ":")
f.P("return &n.rx.", messageField(m), ", true")
}
f.P("default:")
f.P("return nil, false")
f.P("}")
f.P("}")
f.P()
f.P("func (n *", nodeStruct(n), ") TransmittedMessages() []canrunner.TransmittedMessage {")
f.P("return []canrunner.TransmittedMessage{")
for _, m := range txMessages {
f.P("&n.tx.", messageField(m), ",")
}
f.P("}")
f.P("}")
f.P()
for _, m := range rxMessages {
f.P("type ", rxMessageStruct(n, m), " struct {")
f.P(messageStruct(m))
f.P("receiveTime time.Time")
f.P("afterReceiveHook func(context.Context) error")
f.P("}")
f.P()
f.P("func (m *", rxMessageStruct(n, m), ") init() {")
f.P("m.afterReceiveHook = func(context.Context) error { return nil }")
f.P("}")
f.P()
f.P("func (m *", rxMessageStruct(n, m), ") SetAfterReceiveHook(h func(context.Context) error) {")
f.P("m.afterReceiveHook = h")
f.P("}")
f.P()
f.P("func (m *", rxMessageStruct(n, m), ") AfterReceiveHook() func(context.Context) error {")
f.P("return m.afterReceiveHook")
f.P("}")
f.P()
f.P("func (m *", rxMessageStruct(n, m), ") ReceiveTime() time.Time {")
f.P("return m.receiveTime")
f.P("}")
f.P()
f.P("func (m *", rxMessageStruct(n, m), ") SetReceiveTime(t time.Time) {")
f.P("m.receiveTime = t")
f.P("}")
f.P()
f.P("var _ canrunner.ReceivedMessage = &", rxMessageStruct(n, m), "{}")
f.P()
}
for _, m := range txMessages {
f.P("type ", txMessageStruct(n, m), " struct {")
f.P(messageStruct(m))
f.P("transmitTime time.Time")
f.P("beforeTransmitHook func(context.Context) error")
f.P("isCyclicEnabled bool")
f.P("wakeUpChan chan struct{}")
f.P("transmitEventChan chan struct{}")
f.P("}")
f.P()
f.P("var _ ", txMessageInterface(n, m), " = &", txMessageStruct(n, m), "{}")
f.P("var _ canrunner.TransmittedMessage = &", txMessageStruct(n, m), "{}")
f.P()
f.P("func (m *", txMessageStruct(n, m), ") init() {")
f.P("m.beforeTransmitHook = func(context.Context) error { return nil }")
f.P("m.wakeUpChan = make(chan struct{}, 1)")
f.P("m.transmitEventChan = make(chan struct{})")
f.P("}")
f.P()
f.P("func (m *", txMessageStruct(n, m), ") SetBeforeTransmitHook(h func(context.Context) error) {")
f.P("m.beforeTransmitHook = h")
f.P("}")
f.P()
f.P("func (m *", txMessageStruct(n, m), ") BeforeTransmitHook() func(context.Context) error {")
f.P("return m.beforeTransmitHook")
f.P("}")
f.P()
f.P("func (m *", txMessageStruct(n, m), ") TransmitTime() time.Time {")
f.P("return m.transmitTime")
f.P("}")
f.P()
f.P("func (m *", txMessageStruct(n, m), ") SetTransmitTime(t time.Time) {")
f.P("m.transmitTime = t")
f.P("}")
f.P()
f.P("func (m *", txMessageStruct(n, m), ") IsCyclicTransmissionEnabled() bool {")
f.P("return m.isCyclicEnabled")
f.P("}")
f.P()
f.P("func (m *", txMessageStruct(n, m), ") SetCyclicTransmissionEnabled(b bool) {")
f.P("m.isCyclicEnabled = b")
f.P("select {")
f.P("case m.wakeUpChan <-struct{}{}:")
f.P("default:")
f.P("}")
f.P("}")
f.P()
f.P("func (m *", txMessageStruct(n, m), ") WakeUpChan() <-chan struct{} {")
f.P("return m.wakeUpChan")
f.P("}")
f.P()
f.P("func (m *", txMessageStruct(n, m), ") Transmit(ctx context.Context) error {")
f.P("select {")
f.P("case m.transmitEventChan <- struct{}{}:")
f.P("return nil")
f.P("case <-ctx.Done():")
f.P(`return fmt.Errorf("event-triggered transmit of `, m.Name, `: %w", ctx.Err())`)
f.P("}")
f.P("}")
f.P()
f.P("func (m *", txMessageStruct(n, m), ") TransmitEventChan() <-chan struct{} {")
f.P("return m.transmitEventChan")
f.P("}")
f.P()
f.P("var _ canrunner.TransmittedMessage = &", txMessageStruct(n, m), "{}")
f.P()
}
}
func txGroupInterface(n *descriptor.Node) string {
return n.Name + "_Tx"
}
func txGroupStruct(n *descriptor.Node) string {
return "xxx_" + n.Name + "_Tx"
}
func rxGroupInterface(n *descriptor.Node) string {
return n.Name + "_Rx"
}
func rxGroupStruct(n *descriptor.Node) string {
return "xxx_" + n.Name + "_Rx"
}
func rxMessageInterface(n *descriptor.Node, m *descriptor.Message) string {
return n.Name + "_Rx_" + m.Name
}
func rxMessageStruct(n *descriptor.Node, m *descriptor.Message) string {
return "xxx_" + n.Name + "_Rx_" + m.Name
}
func txMessageInterface(n *descriptor.Node, m *descriptor.Message) string {
return n.Name + "_Tx_" + m.Name
}
func txMessageStruct(n *descriptor.Node, m *descriptor.Message) string {
return "xxx_" + n.Name + "_Tx_" + m.Name
}
func collectTxMessages(d *descriptor.Database, n *descriptor.Node) []*descriptor.Message {
tx := make([]*descriptor.Message, 0, len(d.Messages))
for _, m := range d.Messages {
if m == nil {
continue
}
if m.SenderNode == n.Name && m.SendType != descriptor.SendTypeNone {
tx = append(tx, m)
}
}
return tx
}
func collectRxMessages(d *descriptor.Database, n *descriptor.Node) []*descriptor.Message {
rx := make([]*descriptor.Message, 0, len(d.Messages))
Loop:
for _, m := range d.Messages {
if m == nil {
continue
}
for _, s := range m.Signals {
for _, node := range s.ReceiverNodes {
if node != n.Name {
continue
}
rx = append(rx, m)
continue Loop
}
}
}
return rx
}
func hasPhysicalRepresentation(s *descriptor.Signal) bool {
hasScale := s.Scale != 0 && s.Scale != 1
hasOffset := s.Offset != 0
hasRange := s.Min != 0 || s.Max != 0
var hasConstrainedRange bool
if s.IsSigned {
hasConstrainedRange = s.Min > float64(s.MinSigned()) || s.Max < float64(s.MaxSigned())
} else {
hasConstrainedRange = s.Min > 0 || s.Max < float64(s.MaxUnsigned())
}
return hasScale || hasOffset || hasRange && hasConstrainedRange
}
func hasCustomType(s *descriptor.Signal) bool {
return len(s.ValueDescriptions) > 0
}
func hasSendType(d *descriptor.Database) bool {
for _, m := range d.Messages {
if m == nil {
continue
}
if m.SendType != descriptor.SendTypeNone {
return true
}
}
return false
}
func signalType(m *descriptor.Message, s *descriptor.Signal) string {
if hasCustomType(s) {
return m.Name + "_" + s.Name
}
return signalPrimitiveType(s).String()
}
func signalPrimitiveType(s *descriptor.Signal) types.Type {
var t types.BasicKind
switch {
case s.Length == 1:
t = types.Bool
case s.Length <= 8 && s.IsSigned:
t = types.Int8
case s.Length <= 8:
t = types.Uint8
case s.Length <= 16 && s.IsSigned:
t = types.Int16
case s.Length <= 16:
t = types.Uint16
case s.Length <= 32 && s.IsSigned:
t = types.Int32
case s.Length <= 32:
t = types.Uint32
case s.Length <= 64 && s.IsSigned:
t = types.Int64
default:
t = types.Uint64
}
return types.Typ[t]
}
func signalPrimitiveSuperType(s *descriptor.Signal) types.Type {
var t types.BasicKind
switch {
case s.Length == 1:
t = types.Bool
case s.IsSigned:
t = types.Int64
default:
t = types.Uint64
}
return types.Typ[t]
}
func signalSuperType(s *descriptor.Signal) string {
switch {
case s.Length == 1:
return "Bool"
case s.IsSigned:
return "Signed"
default:
return "Unsigned"
}
}
func nodeInterface(n *descriptor.Node) string {
return n.Name
}
func nodeStruct(n *descriptor.Node) string {
return "xxx_" + n.Name
}
func messageStruct(m *descriptor.Message) string {
return m.Name
}
func messageReaderInterface(m *descriptor.Message) string {
return m.Name + "Reader"
}
func messageWriterInterface(m *descriptor.Message) string {
return m.Name + "Writer"
}
func messageField(m *descriptor.Message) string {
return "xxx_" + m.Name
}
func signalField(s *descriptor.Signal) string {
return "xxx_" + s.Name
}
func nodeDescriptor(n *descriptor.Node) string {
return "Nodes()." + n.Name
}
func messageDescriptor(m *descriptor.Message) string {
return "Messages()." + m.Name
}
func signalDescriptor(m *descriptor.Message, s *descriptor.Signal) string {
return messageDescriptor(m) + "." + s.Name
}

View File

@@ -0,0 +1,18 @@
package generate
import (
"os"
"testing"
"gotest.tools/v3/assert"
)
func runTestInDir(t *testing.T, dir string) func() {
// change working directory to project root
wd, err := os.Getwd()
assert.NilError(t, err)
assert.NilError(t, os.Chdir(dir))
return func() {
assert.NilError(t, os.Chdir(wd))
}
}

View File

@@ -0,0 +1,17 @@
package identifiers
import "unicode"
func IsCamelCase(s string) bool {
i := 0
for _, r := range s {
if unicode.IsDigit(r) {
continue
}
if i == 0 && !unicode.IsUpper(r) || !IsAlphaChar(r) && !IsNumChar(r) {
return false
}
i++
}
return true
}

View File

@@ -0,0 +1,18 @@
package identifiers
import (
"testing"
"gotest.tools/v3/assert"
)
func TestIsCamelCase(t *testing.T) {
assert.Assert(t, IsCamelCase("SOC"))
assert.Assert(t, IsCamelCase("Camel"))
assert.Assert(t, IsCamelCase("CamelCase"))
assert.Assert(t, IsCamelCase("111CamelCaseNr"))
assert.Assert(t, !IsCamelCase("camelCase"))
assert.Assert(t, !IsCamelCase("snake_case"))
assert.Assert(t, !IsCamelCase("kebab-case"))
assert.Assert(t, !IsCamelCase("111camelCaseNr"))
}

View File

@@ -0,0 +1,9 @@
package identifiers
func IsAlphaChar(r rune) bool {
return ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z')
}
func IsNumChar(r rune) bool {
return '0' <= r && r <= '9'
}

View File

@@ -0,0 +1,23 @@
package identifiers
import (
"testing"
"gotest.tools/v3/assert"
)
func TestIsAlphaChar(t *testing.T) {
assert.Assert(t, IsAlphaChar('b'))
assert.Assert(t, IsAlphaChar('C'))
assert.Assert(t, !IsAlphaChar('Ö'))
assert.Assert(t, !IsAlphaChar('_'))
}
func TestIsNumChar(t *testing.T) {
assert.Assert(t, IsNumChar('0'))
assert.Assert(t, IsNumChar('1'))
assert.Assert(t, IsNumChar('2'))
assert.Assert(t, IsNumChar('9'))
assert.Assert(t, !IsNumChar('/'))
assert.Assert(t, !IsNumChar('a'))
}

View File

@@ -0,0 +1,50 @@
// Package reinterpret provides primitives for reinterpreting arbitrary-length values as signed or unsigned.
package reinterpret
// AsSigned reinterprets the provided unsigned value as a signed value.
func AsSigned(unsigned uint64, bits uint8) 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)
}
}
// AsUnsigned reinterprets the provided signed value as an unsigned value.
func AsUnsigned(signed int64, bits uint8) uint64 {
switch bits {
case 8:
return uint64(uint8(int8(signed)))
case 16:
return uint64(uint16(int16(signed)))
case 32:
return uint64(uint32(int32(signed)))
case 64:
return uint64(signed)
default:
// calculate bit mask for extracting relevant bits
valueBitMask := uint64(1<<bits) - 1
// extract relevant bits
return uint64(signed) & valueBitMask
}
}

View File

@@ -0,0 +1,68 @@
package reinterpret
import (
"fmt"
"testing"
"gotest.tools/v3/assert"
)
func TestReinterpretSign(t *testing.T) {
for _, tt := range []struct {
unsigned uint64
length uint8
signed int64
}{
// -1, byte aligned
{unsigned: 0xf, length: 4, signed: -1},
{unsigned: 0xff, length: 8, signed: -1},
{unsigned: 0xfff, length: 12, signed: -1},
{unsigned: 0xffff, length: 16, signed: -1},
{unsigned: 0xfffff, length: 20, signed: -1},
{unsigned: 0xffffff, length: 24, signed: -1},
{unsigned: 0xfffffff, length: 28, signed: -1},
{unsigned: 0xffffffff, length: 32, signed: -1},
{unsigned: 0xfffffffff, length: 36, signed: -1},
{unsigned: 0xffffffffff, length: 40, signed: -1},
{unsigned: 0xfffffffffff, length: 44, signed: -1},
{unsigned: 0xffffffffffff, length: 48, signed: -1},
{unsigned: 0xfffffffffffff, length: 52, signed: -1},
{unsigned: 0xffffffffffffff, length: 56, signed: -1},
{unsigned: 0xfffffffffffffff, length: 60, signed: -1},
{unsigned: 0xffffffffffffffff, length: 64, signed: -1},
// 3 bits
{unsigned: 0x0, length: 3, signed: 0},
{unsigned: 0x1, length: 3, signed: 1},
{unsigned: 0x2, length: 3, signed: 2},
{unsigned: 0x3, length: 3, signed: 3},
{unsigned: 0x4, length: 3, signed: -4},
{unsigned: 0x5, length: 3, signed: -3},
{unsigned: 0x6, length: 3, signed: -2},
{unsigned: 0x7, length: 3, signed: -1},
// 4 bits
{unsigned: 0x0, length: 4, signed: 0},
{unsigned: 0x1, length: 4, signed: 1},
{unsigned: 0x2, length: 4, signed: 2},
{unsigned: 0x3, length: 4, signed: 3},
{unsigned: 0x4, length: 4, signed: 4},
{unsigned: 0x5, length: 4, signed: 5},
{unsigned: 0x6, length: 4, signed: 6},
{unsigned: 0x7, length: 4, signed: 7},
{unsigned: 0x8, length: 4, signed: -8},
{unsigned: 0x9, length: 4, signed: -7},
{unsigned: 0xa, length: 4, signed: -6},
{unsigned: 0xb, length: 4, signed: -5},
{unsigned: 0xc, length: 4, signed: -4},
{unsigned: 0xd, length: 4, signed: -3},
{unsigned: 0xe, length: 4, signed: -2},
{unsigned: 0xf, length: 4, signed: -1},
} {
tt := tt
t.Run(fmt.Sprintf("%+v", tt), func(t *testing.T) {
assert.Equal(t, tt.signed, AsSigned(tt.unsigned, tt.length))
assert.Equal(t, tt.unsigned, AsUnsigned(tt.signed, tt.length))
assert.Equal(t, tt.signed, AsSigned(AsUnsigned(tt.signed, tt.length), tt.length))
assert.Equal(t, tt.unsigned, AsUnsigned(AsSigned(tt.unsigned, tt.length), tt.length))
})
}
}