aboutsummaryrefslogtreecommitdiffstats
path: root/sw/cal/signal.go
blob: 368b848f17bc295cc6ddf6ce4c0f76f34dd63d66 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package main

import (
	bin "encoding/binary"
	"fmt"
	"math"

	"go.einride.tech/can"
	"go.einride.tech/can/pkg/dbc"

	"git.samanthony.xyz/can_gauge_interface/sw/cal/canbus"
)

const (
	sigCtrlId   uint32 = 0x1272100
	sigCtrlMask uint32 = 0x1FFFF00

	exide = 1 << 31
)

type SignalDef struct {
	index       uint8
	id          uint32
	isExtended  bool
	name        string
	start, size uint8
	isBigEndian bool
	isSigned    bool
}

func NewSignalDef(index uint8, msg *dbc.MessageDef, sig dbc.SignalDef) (SignalDef, error) {
	if sig.StartBit > math.MaxUint8 {
		return SignalDef{}, fmt.Errorf("%s: start bit out of range: %d", sig.Name, sig.StartBit)
	}
	if sig.Size > math.MaxUint8 {
		return SignalDef{}, fmt.Errorf("%s: size out of range: %d", sig.Name, sig.Size)
	}

	return SignalDef{
		index,
		uint32(msg.MessageID) & extMask,
		msg.MessageID.IsExtended(),
		string(sig.Name),
		uint8(sig.StartBit),
		uint8(sig.Size),
		sig.IsBigEndian,
		sig.IsSigned,
	}, nil
}

// Transmit a signal's encoding in a Signal Control frame
// so the Interface can store it in its EEPROM.
func (sig SignalDef) SendEncoding(bus canbus.Bus) error {
	req := SignalControlRequest{sig.index}
	reply := &SignalDef{}
	isReply := func(reply *SignalDef) bool { return true }
	return sendCtrlFrame(sig, req, reply, bus, isReply, verifySigCtrlReply)
}

// Verify that the response to a Signal Control REMOTE REQUEST
// is the same as what was commanded to be written.
func verifySigCtrlReply(cmd SignalDef, reply *SignalDef) bool {
	reply.index = cmd.index // ignore index and name of reply
	reply.name = cmd.name
	return *reply == cmd
}

func (sig SignalDef) MarshalFrame() (can.Frame, error) {
	var data [8]byte
	if _, err := bin.Encode(data[0:4], bin.BigEndian, uint32(sig.id)&extMask); err != nil {
		return can.Frame{}, err
	}
	if sig.isExtended {
		data[0] |= 0x80 // EXIDE
	}
	data[4] = uint8(sig.start)
	data[5] = uint8(sig.size)
	if !sig.isBigEndian {
		data[6] |= 0x80
	}
	if sig.isSigned {
		data[6] |= 0x40
	}
	return can.Frame{
		ID:         sigCtrlId | uint32(sig.index&0xF),
		Length:     7,
		Data:       data,
		IsExtended: true,
	}, nil
}

func (sig *SignalDef) UnmarshalFrame(frame can.Frame) error {
	if !frame.IsExtended || frame.ID&sigCtrlMask != sigCtrlId {
		return errWrongId
	}
	if frame.Length != 7 {
		return fmt.Errorf("wrong DLC for Signal Control frame: %d", frame.Length)
	}
	sig.index = uint8(frame.ID & 0xF)
	var id uint32
	if _, err := bin.Decode(frame.Data[0:4], bin.BigEndian, &id); err != nil {
		return err
	}
	sig.id = id & extMask
	sig.isExtended = id&exide != 0
	sig.start = frame.Data[4]
	sig.size = frame.Data[5]
	sig.isBigEndian = frame.Data[6]&0x80 == 0
	sig.isSigned = frame.Data[6]&0x40 != 0
	return nil
}

// SignalControlRequest is a Signal Control REMOTE REQUEST frame.
type SignalControlRequest struct {
	sigIndex uint8
}

func (r SignalControlRequest) MarshalFrame() (can.Frame, error) {
	return can.Frame{
		ID:         sigCtrlId | uint32(r.sigIndex&0xF),
		IsRemote:   true,
		IsExtended: true,
	}, nil
}