github.com/simpleiot/simpleiot@v0.18.3/client/serial_test.go (about) 1 package client_test 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "strconv" 8 "testing" 9 "time" 10 11 "github.com/simpleiot/simpleiot/client" 12 "github.com/simpleiot/simpleiot/data" 13 "github.com/simpleiot/simpleiot/server" 14 "github.com/simpleiot/simpleiot/test" 15 ) 16 17 func TestSerial(t *testing.T) { 18 // Start up a SIOT test server for this test 19 nc, root, stop, err := server.TestServer() 20 _ = nc 21 22 if err != nil { 23 t.Fatal("Error starting test server: ", err) 24 } 25 26 defer stop() 27 28 // the test.Fifo is used to emulate a serial port 29 // channel during this test. The A side is written by the 30 // this test and simulates MCU writes. The B side is written by the serial 31 // client. 32 fifo, err := test.NewFifoA("serialfifo") 33 if err != nil { 34 t.Fatal("Error starting fifo: ", err) 35 } 36 37 fifoW := client.NewCobsWrapper(fifo, 500) 38 defer fifoW.Close() 39 40 serialTest := client.SerialDev{ 41 ID: "ID-serial", 42 Parent: root.ID, 43 Description: "test serial", 44 // when Port is set to the magic value of "serialfifo", the serial 45 // client opens a unix fifo instead of a real serial port. This allows 46 // us to send/receive data to/from serial client during 47 // testing without needing real serial hardware. 48 Port: "serialfifo", 49 // You can set debug to increase debugging level 50 Debug: 4, 51 } 52 53 // hydrate database with test data 54 err = client.SendNodeType(nc, serialTest, "test") 55 if err != nil { 56 t.Fatal("Error sending node: ", err) 57 } 58 59 // set up watcher for node 60 getNode, stopWatcher, err := client.NodeWatcher[client.SerialDev](nc, serialTest.ID, serialTest.Parent) 61 if err != nil { 62 t.Fatal("Error setting up node watcher") 63 } 64 65 defer stopWatcher() 66 67 start := time.Now() 68 69 // wait for node to be populated 70 for { 71 cur := getNode() 72 if cur.ID == serialTest.ID { 73 break 74 } 75 if time.Since(start) > time.Second { 76 t.Fatal("Timeout waiting for serial node") 77 } 78 <-time.After(time.Millisecond * 10) 79 } 80 81 readCh := make(chan []byte) 82 var readData []byte 83 84 mcuReadSerial := func() { 85 buf := make([]byte, 200) 86 c, err := fifoW.Read(buf) 87 if err != nil { 88 fmt.Println("Error reading response from client: ", err) 89 } 90 buf = buf[:c] 91 readCh <- buf 92 } 93 94 // dump timeSync package from client 95 go mcuReadSerial() 96 97 select { 98 case <-time.After(time.Second): 99 t.Fatal("Timeout waiting for timeSync packet") 100 case <-readCh: 101 // all is well 102 } 103 104 // send an ascii log message to the serial client 105 log.Println("Sending log test message") 106 buf := bytes.NewBuffer([]byte{}) 107 _, _ = buf.Write([]byte{1}) 108 sub := make([]byte, 16) 109 copy(sub, []byte("log")) 110 _, _ = buf.Write(sub) 111 testLog := "Hi there" 112 _, _ = buf.Write([]byte(testLog)) 113 114 _, err = fifoW.Write(buf.Bytes()) 115 if err != nil { 116 t.Error("Error sending packet to fifo: ", err) 117 } 118 119 // wait for a packet to be received 120 start = time.Now() 121 for { 122 cur := getNode() 123 if cur.Rx == 1 && cur.Log == testLog { 124 break 125 } 126 if time.Since(start) > time.Second { 127 t.Fatal("Timeout waiting for log packet") 128 } 129 <-time.After(time.Millisecond * 100) 130 } 131 132 // send a uptime point to the serial client over serial channel 133 uptimeTest := 5523 134 seq := byte(10) 135 uptimePts := data.Points{ 136 {Type: data.PointTypeUptime, Value: float64(uptimeTest)}, 137 } 138 139 uptimePacket, err := client.SerialEncode(seq, "", uptimePts) 140 if err != nil { 141 t.Fatal("Error encoding serial packet: ", err) 142 } 143 144 _, err = fifoW.Write(uptimePacket) 145 if err != nil { 146 t.Fatal("Error writing pb data to fifo: ", err) 147 } 148 149 // wait for point to show up in node 150 start = time.Now() 151 for { 152 cur := getNode() 153 if cur.Uptime == uptimeTest { 154 break 155 } 156 if time.Since(start) > time.Second { 157 t.Fatal("Timeout waiting for uptime to get set") 158 } 159 <-time.After(time.Millisecond * 100) 160 } 161 162 // check for ack response from serial client 163 go mcuReadSerial() 164 165 select { 166 case <-time.After(time.Second): 167 t.Fatal("Timeout waiting for serial response") 168 case readData = <-readCh: 169 // all is well 170 } 171 172 seqR, subjectR, payload, err := client.SerialDecode(readData) 173 if err != nil { 174 t.Error("Error in response: ", err) 175 } 176 177 pointsR, err := data.PbDecodeSerialPoints(payload) 178 if err != nil { 179 t.Errorf("Error decoding serial payload: %v", err) 180 } 181 182 if seq != seqR { 183 t.Error("Sequence in response did not match: ", seq, seqR) 184 } 185 186 if subjectR != "ack" { 187 t.Error("Subject in response is not ack, is: ", subjectR) 188 } 189 190 if len(pointsR) != 0 { 191 t.Error("should be no points in response") 192 } 193 194 // test sending points to MCU 195 pumpSetting := data.Point{Type: "pumpSetting", Value: 233.5, Origin: root.ID} 196 err = client.SendNodePoint(nc, serialTest.ID, pumpSetting, true) 197 if err != nil { 198 t.Fatal("Error sending pumpSetting point: ", err) 199 } 200 201 // the above should trigger a serial packet to get sent to MCU, look for it now 202 go mcuReadSerial() 203 204 select { 205 case <-time.After(time.Second): 206 t.Fatal("Timeout waiting for pump setting at MCU") 207 case readData = <-readCh: 208 // all is well 209 } 210 211 _, _, payload, err = client.SerialDecode(readData) 212 if err != nil { 213 t.Error("Error in response: ", err) 214 } 215 216 pointsR, err = data.PbDecodeSerialPoints(payload) 217 if err != nil { 218 t.Errorf("Error decoding serial payload: %v", err) 219 } 220 221 if len(pointsR) < 1 { 222 t.Error("Did not receive pointsR point") 223 } else { 224 if pointsR[0].Value != pumpSetting.Value { 225 t.Error("Error in pump setting received by MCU") 226 } 227 } 228 } 229 230 func TestSerialLargeMessage(t *testing.T) { 231 // Start up a SIOT test server for this test 232 nc, root, stop, err := server.TestServer() 233 _ = nc 234 235 if err != nil { 236 t.Fatal("Error starting test server: ", err) 237 } 238 239 defer stop() 240 241 // the test.Fifo is used to emulate a serial port 242 // channel during this test. The A side is used by the 243 // this test, and the B side is used by the serial 244 // client. 245 fifo, err := test.NewFifoA("serialfifo") 246 if err != nil { 247 t.Fatal("Error starting fifo: ", err) 248 } 249 250 fifoW := client.NewCobsWrapper(fifo, 500) 251 defer fifoW.Close() 252 253 serialTest := client.SerialDev{ 254 ID: "ID-serial", 255 Parent: root.ID, 256 Description: "test serial", 257 // when Port is set to the magic value of "serialfifo", the serial 258 // client opens a unix fifo instead of a real serial port. This allows 259 // us to send/receive data to/from serial client during 260 // testing without needing real serial hardware. 261 Port: "serialfifo", 262 } 263 264 // hydrate database with test data 265 err = client.SendNodeType(nc, serialTest, "test") 266 if err != nil { 267 t.Fatal("Error sending node: ", err) 268 } 269 270 // set up watcher for node 271 getNode, stopWatcher, err := client.NodeWatcher[client.SerialDev](nc, serialTest.ID, serialTest.Parent) 272 if err != nil { 273 t.Fatal("Error setting up node watcher") 274 } 275 276 defer stopWatcher() 277 278 start := time.Now() 279 280 // wait for node to be populated 281 for { 282 cur := getNode() 283 if cur.ID == serialTest.ID { 284 break 285 } 286 if time.Since(start) > time.Second { 287 t.Fatal("Timeout waiting for serial node") 288 } 289 <-time.After(time.Millisecond * 10) 290 } 291 292 var points data.Points 293 294 for i := 0; i < 10; i++ { 295 points = append(points, data.Point{Type: "testPoint", 296 Key: strconv.Itoa(i), Value: float64(i * 2)}) 297 } 298 299 packet, err := client.SerialEncode(1, "", points) 300 if err != nil { 301 t.Fatal("Error encoding serial packet: ", err) 302 } 303 304 fmt.Println("len(packet): ", len(packet)) 305 fmt.Println("Rx: ", getNode().Rx) 306 307 _, err = fifoW.Write(packet) 308 if err != nil { 309 t.Fatal("Error writing pb data to fifo: ", err) 310 } 311 312 // wait for point to show up in node 313 start = time.Now() 314 for { 315 cur := getNode() 316 if cur.Rx >= 1 { 317 break 318 } 319 if time.Since(start) > time.Second { 320 t.Fatal("Timeout waiting for packet to get set") 321 } 322 <-time.After(time.Millisecond * 100) 323 } 324 }