more api frame stuff
This commit is contained in:
parent
f58938ea6b
commit
67dbb74f77
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
// It encodes and decodes
|
// It encodes and decodes
|
||||||
// API frames from io.Writer and io.Reader by providing a WriteFrame function and
|
// API frames from io.Writer and io.Reader by providing a WriteFrame function and
|
||||||
// a scanner.split function. It also includes
|
// a scanner.split function. It also includes internal packets for using the API.
|
||||||
|
// For end-users, it provides a simple net.Conn-like interface that can write
|
||||||
|
// and read arbitrary bytes (to be used by a higher level protocol)
|
||||||
package xbee
|
package xbee
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// the frames have an outer shell - we will make a function that takes
|
// the frames have an outer shell - we will make a function that takes
|
||||||
|
@ -27,11 +27,10 @@ type Frameable interface {
|
||||||
Bytes() ([]byte, error)
|
Bytes() ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// we also need a way of tracking frame IDs - this is a uint8
|
|
||||||
// it uses a sync.Map
|
|
||||||
|
|
||||||
|
|
||||||
// now we can describe our function that takes a framable and contains it + calculates checksums.
|
// calculateChecksum is a helper function to calculate the 1-byte checksum of a data range.
|
||||||
|
// the data range does not include the start delimiter, or the length uint16 (only the frame payload)
|
||||||
func calculateChecksum(data []byte) byte {
|
func calculateChecksum(data []byte) byte {
|
||||||
var sum byte
|
var sum byte
|
||||||
for _, v := range data {
|
for _, v := range data {
|
||||||
|
@ -40,6 +39,7 @@ func calculateChecksum(data []byte) byte {
|
||||||
return 0xFF - sum
|
return 0xFF - sum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteFrame takes a frameable and writes it out to the given writer.
|
||||||
func WriteFrame(w io.Writer, cmd Frameable) (n int, err error) {
|
func WriteFrame(w io.Writer, cmd Frameable) (n int, err error) {
|
||||||
frame_data, err := cmd.Bytes()
|
frame_data, err := cmd.Bytes()
|
||||||
|
|
||||||
|
@ -90,7 +90,6 @@ const (
|
||||||
|
|
||||||
// AT commands are hard, so let's write out all the major ones here
|
// AT commands are hard, so let's write out all the major ones here
|
||||||
|
|
||||||
|
|
||||||
// Now we will implement receiving packets from a character stream.
|
// Now we will implement receiving packets from a character stream.
|
||||||
// we first need to make a thing that produces frames from a stream using a scanner.
|
// we first need to make a thing that produces frames from a stream using a scanner.
|
||||||
|
|
||||||
|
@ -109,9 +108,13 @@ func xbeeFrameSplit(data []byte, atEOF bool) (advance int, token []byte, err err
|
||||||
}
|
}
|
||||||
|
|
||||||
if startIdx := bytes.IndexByte(data, 0x7E); startIdx >= 0 {
|
if startIdx := bytes.IndexByte(data, 0x7E); startIdx >= 0 {
|
||||||
// we have a start character. get the length.
|
// we have a start character. see if we can get the length.
|
||||||
// we add 4 since start delimiter (1) + length (2) + checksum (1).
|
// we add 4 since start delimiter (1) + length (2) + checksum (1).
|
||||||
// the length inside the packet represents the frame data only.
|
// the length inside the packet represents the frame data only.
|
||||||
|
if len(data[startIdx:]) < 3 {
|
||||||
|
return startIdx, nil, nil
|
||||||
|
}
|
||||||
|
// FIXME: add bounds checking! this can panic.
|
||||||
var frameLen = binary.BigEndian.Uint16(data[startIdx+1:startIdx+3]) + 4
|
var frameLen = binary.BigEndian.Uint16(data[startIdx+1:startIdx+3]) + 4
|
||||||
if len(data[startIdx:]) < int(frameLen) {
|
if len(data[startIdx:]) < int(frameLen) {
|
||||||
// we got the length, but there's not enough data for the frame. we can trim the
|
// we got the length, but there's not enough data for the frame. we can trim the
|
||||||
|
@ -125,3 +128,29 @@ func xbeeFrameSplit(data []byte, atEOF bool) (advance int, token []byte, err err
|
||||||
// we didn't find a start character in our data, so request more. trash everythign given to us
|
// we didn't find a start character in our data, so request more. trash everythign given to us
|
||||||
return len(data), nil, nil
|
return len(data), nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func parseFrame(frame []byte) ([]byte, error) {
|
||||||
|
if frame[0] != 0x7E {
|
||||||
|
return nil, errors.New("incorrect start delimiter")
|
||||||
|
}
|
||||||
|
fsize := len(frame)
|
||||||
|
if calculateChecksum(frame[3:fsize - 1]) != frame[fsize] {
|
||||||
|
return nil, errors.New("checksum mismatch")
|
||||||
|
}
|
||||||
|
return frame[3:fsize - 1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// stackup
|
||||||
|
// low level readwriter (serial or IP socket)
|
||||||
|
// XBee library layer (frame encoding/decoding to/from structs)
|
||||||
|
// xbee conn-like layer (ReadWriter + custom control functions)
|
||||||
|
// application marshalling format (msgpack or json or gob)
|
||||||
|
// application
|
||||||
|
|
||||||
|
|
||||||
|
type XBeeConn interface {
|
||||||
|
io.ReadWriter
|
||||||
|
WriteFrame(Frameable, bool) Frameable
|
||||||
|
}
|
||||||
|
|
|
@ -69,6 +69,26 @@ func Test_xbeeFrameSplit(t *testing.T) {
|
||||||
wantToken: []byte{0x7E, 0x00, 0x02, 0x23, 0x11, 0xCB},
|
wantToken: []byte{0x7E, 0x00, 0x02, 0x23, 0x11, 0xCB},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "trailing start delimiter",
|
||||||
|
args: args{
|
||||||
|
data: []byte{0x53, 0x00, 0x02, 0x23, 0x11, 0x7E},
|
||||||
|
atEOF: false,
|
||||||
|
},
|
||||||
|
wantAdvance: 5,
|
||||||
|
wantToken: nil,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "incomplete length value",
|
||||||
|
args: args{
|
||||||
|
data: []byte{0x53, 0x00, 0x02, 0x23, 0x11, 0x7E, 0x00},
|
||||||
|
atEOF: false,
|
||||||
|
},
|
||||||
|
wantAdvance: 5,
|
||||||
|
wantToken: nil,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package xbee
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RxFrame struct {
|
||||||
|
Source uint64
|
||||||
|
ACK bool
|
||||||
|
BCast bool
|
||||||
|
Payload []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseRxFrame(data []byte) (*RxFrame, error) {
|
||||||
|
// data is the binary that *isn't* part of the frame
|
||||||
|
// i.e it excludes start delimiter, length, and checksum.
|
||||||
|
|
||||||
|
// check the frame type (data[0])
|
||||||
|
if data[0] != byte(RxPkt) && data[0] != byte(RxPktExpl) {
|
||||||
|
return nil, fmt.Errorf("incorrect frame type 0x%x", data[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
rx := &RxFrame{
|
||||||
|
Source: binary.BigEndian.Uint64(data[1:]),
|
||||||
|
Payload: data[12:],
|
||||||
|
}
|
||||||
|
// RX options
|
||||||
|
opt := data[11]
|
||||||
|
// todo: use this
|
||||||
|
fmt.Print(opt)
|
||||||
|
|
||||||
|
return rx, nil
|
||||||
|
}
|
0
internal/xbee/session.go
Normal file
0
internal/xbee/session.go
Normal file
Loading…
Reference in a new issue