-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathframes.go
166 lines (141 loc) · 4.53 KB
/
frames.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Package frames provides useful functions to deal with data frames.
package frames
import "fmt"
// Frame represents a data frame that can be e.g sent by USART.
//
// Frame starts with a header that is always 2 bytes. Header can only contain
// uppercase ASCII letters. Directly afer a header comes length byte which
// describes how long is data. After the length byte comes a plus sign ("+").
// Then comes an arbitrary-length data. Data is terminated with a hash sign
// ("#"). The last byte is a simple 8-bit CRC checksum.
//
// Some example frames (H = header byte, D = data byte, C = CRC byte):
//
// HH4+DDDD#C
//
// LD5+DDDDD#C
//
// XD7+DDDDDDD#C
type Frame []byte
// Header returns frame's header, i.e the first 2 bytes.
//
// If the frame is invalid, it may return a slice of any length.
func (f Frame) Header() []byte {
return f[:2]
}
// LenData returns the length of frame's data in bytes, i.e the third byte.
//
// If the third byte "lies" about the frame's length, then this function will
// return that invalid value.
func (f Frame) LenData() int {
return int(f[2])
}
// Data returns frame's data part from the first byte after a plus sign ("+") up
// to the antepenultimate (last but one - 1) byte.
func (f Frame) Data() []byte {
headerLength := len(f.Header())
begin := headerLength + 2 // example: LD4+DDDD : we want to start from D (so index 4)
end := len(f) - 2
return f[begin:end]
}
// Checksum returns frame's simple CRC checksum, i.e the last byte.
func (f Frame) Checksum() byte {
return f[len(f)-1]
}
// Create creates a new frame. The frame starts with header and contains data.
// Create also calculates the checksum using CalculateChecksum. Data length must
// not overflow byte.
func Create(header [2]byte, data []byte) (frame Frame) {
frame = make(Frame, len(header)+1+1+len(data)+2)
copy(frame[:2], header[:])
frame[len(header)] = byte(len(data))
frame[len(header)+1] = '+'
copy(frame[len(header)+2:len(frame)-2], data)
frame[len(frame)-2] = '#'
frame[len(frame)-1] = CalculateChecksum(frame)
return
}
// Recreate creates a new frame from already available byte buffer. It does not
// check whether buf represents a correct frame. To check if the newly created
// frame is correct, use Verify function. Data length must not overflow byte.
func Recreate(buf []byte) (frame Frame) {
frame = make(Frame, len(buf))
copy(frame[:], buf[:])
return
}
// Assemble creates a frame from already available values.
//
// Deprecated: you should probably just use Recreate.
func Assemble(header [2]byte, length byte, data []byte, checksum byte) (frame Frame) {
frame = make(Frame, len(header)+1+1+len(data)+2)
copy(frame[:2], header[:])
frame[len(header)] = length
frame[len(header)+1] = '+'
copy(frame[len(header)+2:len(frame)-2], data)
frame[len(frame)-2] = '#'
frame[len(frame)-1] = checksum
return
}
// Verify checks whether the frame is valid (i.e of correct format).
//
// The frame must have:
//
// - at 0th and 1st index: a header consisting of uppercase ASCII header or
// numbers
//
// - at 2nd index: "length byte" that is equal to the length of data
//
// - at 3rd index: a plus sign ("+")
//
// - at penultimate position: a hash sign ("#")
//
// - at last position: a simple CRC checksum that must be correct
func Verify(frame Frame) bool {
if len(frame) < 6 {
return false
}
first := frame[0]
valid1 := (first >= 'A' && first <= 'Z') || (first >= '0' && first <= '9')
if !valid1 {
return false
}
second := frame[1]
valid2 := (second >= 'A' && second <= 'Z') || (second >= '0' && second <= '9')
if !valid2 {
return false
}
if frame[2] != byte(frame.LenData()) {
return false
}
if frame.LenData() != len(frame.Data()) {
return false
}
if frame[3] != '+' {
return false
}
if frame[len(frame)-2] != '#' {
return false
}
checksum := CalculateChecksum(frame)
return checksum == frame.Checksum()
}
// CalculateChecksum calculates the simple CRC checksum of frame.
//
// It takes all frame's bytes into account, except the last byte, because the
// last byte is the checksum itself. It does not check whether the frame is
// correct.
func CalculateChecksum(frame Frame) (crc byte) {
crc = frame[0]
for i := 1; i < len(frame)-1; i++ {
crc ^= frame[i]
}
return
}
func (f Frame) String() string {
return fmt.Sprintf("%s+%x#%x", f.Header(), f.Data(), f.Checksum())
}
// DescribeByte prints everything most common representations of a byte. It
// prints b's binary value, decimal, hexadecimal value and ASCII.
func DescribeByte(b byte) string {
return fmt.Sprintf("byte(bin: %08b, dec: %3d, hex: %02x, ASCII: %+q)", b, b, b, b)
}