소스 검색

Started a Go based Link experiment.

Michael Hope 4 년 전
부모
커밋
843a5f1f0b
1개의 변경된 파일235개의 추가작업 그리고 0개의 파일을 삭제
  1. 235 0
      experiments/link.go

+ 235 - 0
experiments/link.go 파일 보기

@@ -0,0 +1,235 @@
1
+package main
2
+
3
+import (
4
+        "github.com/tarm/goserial"
5
+        "log"
6
+	"io"
7
+	"encoding/binary"
8
+	"bytes"
9
+	"time"
10
+	"fmt"
11
+)
12
+
13
+const (
14
+	Mark = '\n'
15
+	Escape = '^'
16
+	Xor = 0x20
17
+)
18
+
19
+type Link struct {
20
+	Received uint
21
+	Escaped uint
22
+
23
+	Overruns uint
24
+	CheckErrors uint
25
+	ShortFrame uint
26
+	Unrecognised uint
27
+	DecodeError uint
28
+}
29
+
30
+type Heartbeat struct {
31
+	Code uint8
32
+	Version uint8
33
+	DeviceId uint8
34
+	Ticks uint8
35
+}
36
+
37
+type Input struct {
38
+	Code uint8
39
+	Channels [6]int8
40
+}
41
+
42
+type Pong struct {
43
+	Code uint8
44
+}
45
+
46
+type Request struct {
47
+	Code uint8
48
+	Requested uint8
49
+}
50
+
51
+type Version struct {
52
+	Code uint8
53
+	Version [18]byte
54
+}
55
+
56
+type State struct {
57
+	Code uint8
58
+	Flags uint8	
59
+}
60
+
61
+type Demand struct {
62
+	Code uint8
63
+	Flags uint8
64
+	Channels [6]int8
65
+}
66
+
67
+var now time.Time
68
+
69
+func init() {
70
+	now = time.Now()
71
+}
72
+
73
+func checksum(frame []byte) byte {
74
+	var sum1, sum2 uint = 0x12, 0x34
75
+
76
+	for _, ch := range frame {
77
+		sum1 += uint(ch)
78
+		if sum1 >= 255 {
79
+			sum1 -= 255
80
+		}
81
+		sum2 += sum1
82
+		if sum2 >= 255 {
83
+			sum2 -= 255
84
+		}
85
+	}
86
+
87
+	return byte(sum1 ^ sum2)
88
+}
89
+
90
+func make_message(code byte) interface{} {
91
+	switch code {
92
+	case 'h':
93
+		return &Heartbeat{}
94
+	case 'i':
95
+		return &Input{}
96
+	case 'P':
97
+		return &Pong{}
98
+	case 'v':
99
+		return &Version{}
100
+	case 's':
101
+		return &State{}
102
+	default:
103
+		return nil
104
+	}
105
+}
106
+
107
+func (link *Link) dispatch(frame []byte) {
108
+	if len(frame) < 2 {
109
+		// Need at least the code and checksum.
110
+		link.ShortFrame += 1
111
+		log.Println("err: shortframe")
112
+	} else {
113
+//		log.Println("frame:", frame)
114
+
115
+		length := len(frame) - 1
116
+		sum := checksum(frame[:length])
117
+		code := frame[0]
118
+		msg := make_message(code)
119
+
120
+		switch {
121
+		case sum != frame[length]:
122
+			// Bad checksum.
123
+			link.CheckErrors += 1
124
+			log.Println("err: checksum", sum, frame[length])
125
+		case msg == nil:
126
+			log.Println("err: unrecognised", code)
127
+			link.Unrecognised += 1
128
+		default:
129
+			src := bytes.NewBuffer(frame[:length])
130
+			err := binary.Read(src, binary.LittleEndian, msg)
131
+
132
+			switch {
133
+			case err != nil:
134
+				link.DecodeError += 1
135
+				log.Println("err: decode error", err, binary.Size(msg), length)
136
+			case src.Len() != 0:
137
+				link.DecodeError += 1
138
+				log.Println("err: unused input while decoding.")
139
+			default:
140
+				log.Println(fmt.Sprintf("%.3f", time.Now().Sub(now).Seconds()), "ok", string(code), msg)
141
+				link.Received += 1
142
+			}
143
+		}
144
+	}
145
+}
146
+
147
+func (link *Link) receive(port io.ReadWriteCloser) {
148
+	got := make([]byte, 1)
149
+        frame := make([]byte, 0)
150
+	rx := make([]byte, 0)
151
+
152
+        var xor byte
153
+
154
+	for {
155
+	        n, err := port.Read(got)
156
+        	if err != nil {
157
+                   log.Fatal(err)
158
+		}
159
+//		log.Println("got", got[:n])
160
+
161
+                for _, ch := range got[:n] {
162
+			rx = append(rx, ch)
163
+			switch ch {
164
+			case Mark:
165
+//				log.Println("rx", len(rx), rx)
166
+				link.dispatch(frame)
167
+				frame = make([]byte, 0)
168
+				rx = make([]byte, 0)
169
+			case Escape:
170
+				xor = Xor
171
+				link.Escaped += 1
172
+			default:
173
+				frame = append(frame, ch ^ xor)
174
+				xor = 0
175
+
176
+				if len(frame) > 100 {
177
+					link.Overruns += 1
178
+					log.Println("err: overrun")
179
+					frame = make([]byte, 0)
180
+				}
181
+                        }
182
+                }
183
+	}
184
+}
185
+
186
+func (link *Link) Send(port io.ReadWriteCloser, msg interface{}) {
187
+	encoded := new(bytes.Buffer)
188
+	binary.Write(encoded, binary.LittleEndian, msg)
189
+	encoded.WriteByte(checksum(encoded.Bytes()))
190
+
191
+	log.Println("send", encoded.Bytes())
192
+
193
+	escaped := make([]byte, 0)
194
+
195
+	for _, ch := range encoded.Bytes() {
196
+		switch ch {
197
+		case Mark, Escape:
198
+			escaped = append(escaped, Escape)
199
+			escaped = append(escaped, ch ^ Xor)
200
+		default:
201
+			escaped = append(escaped, ch)
202
+		}
203
+	}
204
+
205
+	escaped = append(escaped, Mark)
206
+	port.Write(escaped)
207
+}
208
+
209
+func main() {
210
+        c := &serial.Config{Name: "/dev/ttyO4", Baud: 38400}
211
+        s, err := serial.OpenPort(c)
212
+        if err != nil {
213
+                log.Fatal(err)
214
+        }
215
+
216
+	link := &Link{}
217
+        link.receive(s)
218
+// 	var v int8 = -50
219
+
220
+// 	for {
221
+// 		time.Sleep(50 * time.Millisecond)
222
+// 		msg := new(Demand)
223
+// 		msg.Code = 'd'
224
+// 		msg.Flags = 1
225
+// 		msg.Channels[2] = 0
226
+// 		msg.Channels[0] = v
227
+// 		v += 1
228
+// 		if v == 50 {
229
+// 			v = -50
230
+// 		}
231
+
232
+// 		link.Send(s, msg)
233
+// //		link.Send(s, &Request{'R', 'p'})
234
+// 	}
235
+}