github.com/la5nta/wl2k-go@v0.11.8/transport/ardop/frame.go (about)

     1  // Copyright 2015 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
     2  // Use of this source code is governed by the MIT-license that can be
     3  // found in the LICENSE file.
     4  
     5  package ardop
     6  
     7  import (
     8  	"bufio"
     9  	"encoding/binary"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"regexp"
    14  )
    15  
    16  type frame interface{}
    17  
    18  type dFrame struct {
    19  	dataType string
    20  	data     []byte
    21  }
    22  
    23  func (f dFrame) ARQFrame() bool { return f.dataType == "ARQ" }
    24  func (f dFrame) FECFrame() bool { return f.dataType == "FEC" }
    25  func (f dFrame) ErrFrame() bool { return f.dataType == "ERR" }
    26  func (f dFrame) IDFrame() bool  { return f.dataType == "IDF" }
    27  
    28  type cmdFrame string
    29  
    30  func (f cmdFrame) Parsed() ctrlMsg { return parseCtrlMsg(string(f)) }
    31  
    32  func writeCtrlFrame(isTCP bool, w io.Writer, format string, params ...interface{}) error {
    33  	var prefix string
    34  	if !isTCP {
    35  		prefix = "C:"
    36  	}
    37  
    38  	payload := fmt.Sprintf(format+"\r", params...)
    39  	_, err := fmt.Fprint(w, prefix+payload)
    40  
    41  	if !isTCP && err == nil {
    42  		sum := crc16Sum([]byte(payload))
    43  		err = binary.Write(w, binary.BigEndian, sum)
    44  	}
    45  
    46  	return err
    47  }
    48  
    49  func readFrameOfType(fType byte, reader *bufio.Reader, isTCP bool) (frame, error) {
    50  	var err error
    51  	var data []byte
    52  	switch fType {
    53  	case '*': // !isTCP
    54  		fType, err = reader.ReadByte()
    55  		if err != nil {
    56  			return nil, err
    57  		}
    58  		reader.ReadByte() // Discard ';'. (TODO: Use reader.Discard(1) when we drop support for Go <= 1.4).
    59  		return readFrameOfType(fType, reader, isTCP)
    60  	case 'c':
    61  		data, err = reader.ReadBytes('\r')
    62  	case 'd':
    63  		// Peek length
    64  		peeked, err := reader.Peek(2)
    65  		if err != nil {
    66  			return nil, err
    67  		}
    68  		length := binary.BigEndian.Uint16(peeked) + 2 // +2 to include the length bytes
    69  
    70  		// actual data
    71  		data = make([]byte, length)
    72  		var n int
    73  		for read := 0; read < int(length) && err == nil; {
    74  			n, err = reader.Read(data[read:])
    75  			read += n
    76  		}
    77  	default:
    78  		return nil, fmt.Errorf("Unexpected frame type %c", fType)
    79  	}
    80  
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	// Verify CRC sums
    86  	if !isTCP {
    87  		sumBytes := make([]byte, 2)
    88  		reader.Read(sumBytes)
    89  		crc := binary.BigEndian.Uint16(sumBytes)
    90  		if crc16Sum(data) != crc {
    91  			return nil, ErrChecksumMismatch
    92  		}
    93  	}
    94  
    95  	switch fType {
    96  	case 'c':
    97  		data = data[:len(data)-1] // Trim \r
    98  		return cmdFrame(string(data)), nil
    99  	case 'd':
   100  		return dFrame{dataType: string(data[2:5]), data: data[5:]}, nil
   101  	default:
   102  		panic("not possible")
   103  	}
   104  }
   105  
   106  // Data example: " LA5NTA:[JP20QE] "
   107  var reID = regexp.MustCompile(`(\w+)[:\s]*\[(\w+)\]`)
   108  
   109  func parseIDFrame(df dFrame) (callsign, gridsquare string, err error) {
   110  	if !df.IDFrame() {
   111  		return "", "", errors.New("Unexpected frame type")
   112  	}
   113  
   114  	matches := reID.FindSubmatch(df.data)
   115  	if len(matches) != 3 {
   116  		return "", "", errors.New("Unexpected ID format")
   117  	}
   118  
   119  	return string(matches[1]), string(matches[2]), nil
   120  }