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  }