github.com/simpleiot/simpleiot@v0.18.3/client/serial-wrapper.go (about)

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  
     9  	"github.com/kjx98/crc16"
    10  	"github.com/simpleiot/simpleiot/data"
    11  	"github.com/simpleiot/simpleiot/internal/pb"
    12  	"google.golang.org/protobuf/proto"
    13  )
    14  
    15  // Packet format is:
    16  //   - sequence #: 1 byte
    17  //   - subject (16 bytes)
    18  //   - protobuf (serial) payload
    19  //   - crc: 2 bytes (CCITT)
    20  
    21  // SerialEncode can be used in a client to encode points sent over a serial link.
    22  func SerialEncode(seq byte, subject string, points data.Points) ([]byte, error) {
    23  	var ret bytes.Buffer
    24  	ret.WriteByte(seq)
    25  
    26  	sub := make([]byte, 16)
    27  
    28  	if len(subject) > 16 {
    29  		return []byte{},
    30  			fmt.Errorf("SerialEncode Error: length of subject %v is longer than 20 bytes", subject)
    31  	}
    32  
    33  	copy(sub, []byte(subject))
    34  
    35  	_, err := ret.Write(sub)
    36  	if err != nil {
    37  		return []byte{}, fmt.Errorf("SerialEncode: error writing to buffer: %v", err)
    38  	}
    39  
    40  	pbPoints := make([]*pb.SerialPoint, len(points))
    41  	for i, p := range points {
    42  		pPb, err := p.ToSerial()
    43  		if err != nil {
    44  			return nil, err
    45  		}
    46  		pbPoints[i] = &pPb
    47  	}
    48  
    49  	pbSerial := &pb.SerialPoints{
    50  		Points: pbPoints,
    51  	}
    52  
    53  	pbSerialBytes, err := proto.Marshal(pbSerial)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	ret.Write(pbSerialBytes)
    59  
    60  	crc := crc16.ChecksumCCITT(ret.Bytes())
    61  
    62  	err = binary.Write(&ret, binary.LittleEndian, crc)
    63  
    64  	return ret.Bytes(), err
    65  }
    66  
    67  // SerialDecode can be used to decode serial data in a client.
    68  // returns seq, subject, payload
    69  func SerialDecode(d []byte) (byte, string, []byte, error) {
    70  	l := len(d)
    71  
    72  	if l < 1 {
    73  		return 0, "", nil, errors.New("Not enough data")
    74  	}
    75  
    76  	if l < (1 + 16) {
    77  		return d[0], "", nil, errors.New("Not enough data")
    78  	}
    79  
    80  	// try to extract subject
    81  	subject := string(bytes.Trim(d[1:17], "\x00"))
    82  	end := l
    83  
    84  	if subject != "log" {
    85  		// check CRC
    86  		end -= 2
    87  		if len(d) < (1 + 2 + 16) {
    88  			return d[0], "", nil, errors.New("Not enough data")
    89  		}
    90  
    91  		crc := binary.LittleEndian.Uint16(d[l-2:])
    92  		crcCalc := crc16.ChecksumCCITT(d[:l-2])
    93  		if crc != crcCalc {
    94  			return d[0], "", nil, errors.New("CRC check failed")
    95  		}
    96  	}
    97  
    98  	// try to extract protobuf
    99  	payload := d[17:end]
   100  
   101  	return d[0], subject, payload, nil
   102  }