-
Notifications
You must be signed in to change notification settings - Fork 38
/
frame.go
135 lines (128 loc) · 3.15 KB
/
frame.go
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
125
126
127
128
129
130
131
132
133
134
135
package can
import (
"encoding/hex"
"fmt"
"strconv"
"strings"
)
const (
idBits = 11
extendedIDBits = 29
)
// CAN format constants.
const (
MaxID = 0x7ff
MaxExtendedID = 0x1fffffff
)
// Frame represents a CAN frame.
//
// A Frame is intentionally designed to fit into 16 bytes on common architectures
// and is therefore amenable to pass-by-value and judicious copying.
type Frame struct {
// ID is the CAN ID
ID uint32
// Length is the number of bytes of data in the frame.
Length uint8
// Data is the frame data.
Data Data
// IsRemote is true for remote frames.
IsRemote bool
// IsExtended is true for extended frames, i.e. frames with 29-bit IDs.
IsExtended bool
}
// Validate returns an error if the Frame is not a valid CAN frame.
func (f *Frame) Validate() error {
// Validate: ID
if f.IsExtended && f.ID > MaxExtendedID {
return fmt.Errorf(
"invalid extended CAN id: %v does not fit in %v bits",
f.ID,
extendedIDBits,
)
} else if !f.IsExtended && f.ID > MaxID {
return fmt.Errorf(
"invalid standard CAN id: %v does not fit in %v bits",
f.ID,
idBits,
)
}
// Validate: Data
if f.Length > MaxDataLength {
return fmt.Errorf("invalid data length: %v", f.Length)
}
return nil
}
// String returns an ASCII representation the CAN frame.
//
// Format:
//
// ([0-9A-F]{3}|[0-9A-F]{3})#(R[0-8]?|[0-9A-F]{0,16})
//
// The format is compatible with the candump(1) log file format.
func (f Frame) String() string {
var id string
if f.IsExtended {
id = fmt.Sprintf("%08X", f.ID)
} else {
id = fmt.Sprintf("%03X", f.ID)
}
if f.IsRemote && f.Length == 0 {
return id + "#R"
} else if f.IsRemote {
return id + "#R" + strconv.Itoa(int(f.Length))
}
return id + "#" + strings.ToUpper(hex.EncodeToString(f.Data[:f.Length]))
}
// UnmarshalString sets *f using the provided ASCII representation of a Frame.
func (f *Frame) UnmarshalString(s string) error {
// Split split into parts
parts := strings.Split(s, "#")
if len(parts) != 2 {
return fmt.Errorf("invalid frame format: %v", s)
}
idPart, dataPart := parts[0], parts[1]
var frame Frame
// Parse: IsExtended
if len(idPart) != 3 && len(idPart) != 8 {
return fmt.Errorf("invalid ID length: %v", s)
}
frame.IsExtended = len(idPart) == 8
// Parse: ID
id, err := strconv.ParseUint(idPart, 16, 32)
if err != nil {
return fmt.Errorf("invalid frame ID: %v", s)
}
frame.ID = uint32(id)
if len(dataPart) == 0 {
*f = frame
return nil
}
// Parse: IsRemote
if dataPart[0] == 'R' {
frame.IsRemote = true
if len(dataPart) > 2 {
return fmt.Errorf("invalid remote length: %v", s)
} else if len(dataPart) == 2 {
dataLength, err := strconv.Atoi(dataPart[1:2])
if err != nil {
return fmt.Errorf("invalid remote length: %v: %w", s, err)
}
frame.Length = uint8(dataLength)
}
*f = frame
return nil
}
// Parse: Length
if len(dataPart) > 16 || len(dataPart)%2 != 0 {
return fmt.Errorf("invalid data length: %v", s)
}
frame.Length = uint8(len(dataPart) / 2)
// Parse: Data
decodedData, err := hex.DecodeString(dataPart)
if err != nil {
return fmt.Errorf("invalid data: %v: %w", s, err)
}
copy(frame.Data[:], decodedData)
*f = frame
return nil
}