move tihngs
This commit is contained in:
parent
ba288f2959
commit
c7cda9db7a
|
@ -1,11 +1,69 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/kschamplin/gotelem/internal/gotelem"
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
const xbeeCategory = "XBee settings"
|
||||
|
||||
var serveCmd = &cli.Command{
|
||||
Name: "serve",
|
||||
Aliases: []string{"server", "s"},
|
||||
Usage: "Start a telemetry server",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{Name: "xbee", Aliases: []string{"x"}, Usage: "Find and connect to an XBee"},
|
||||
},
|
||||
Action: func(ctx *cli.Context) error {
|
||||
serve()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
type session struct {
|
||||
conn net.Conn
|
||||
send chan gotelem.Body
|
||||
recv chan gotelem.Body
|
||||
quit chan bool
|
||||
}
|
||||
|
||||
func serve() {
|
||||
ln, err := net.Listen("tcp", ":8082")
|
||||
if err != nil {
|
||||
fmt.Printf("Error listening: %v\n", err)
|
||||
}
|
||||
fmt.Printf("Listening on :8082\n")
|
||||
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
fmt.Printf("error accepting: %v\n", err)
|
||||
}
|
||||
go handleCon(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func handleCon(conn net.Conn) {
|
||||
// reader := msgp.NewReader(conn)
|
||||
writer := msgp.NewWriter(conn)
|
||||
for {
|
||||
// data := telemnet.StatusBody{
|
||||
// BatteryPct: 1.2,
|
||||
// ErrCode: 0,
|
||||
// }
|
||||
// data.EncodeMsg(writer)
|
||||
data := gotelem.StatusBody{
|
||||
BatteryPct: 1.2,
|
||||
ErrCode: 0,
|
||||
}
|
||||
data.EncodeMsg(writer)
|
||||
writer.Flush()
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
|
4
go.mod
4
go.mod
|
@ -9,6 +9,10 @@ require (
|
|||
|
||||
require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/philhofer/fwd v1.1.2 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/tinylib/msgp v1.1.8 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/tools v0.8.0 // indirect
|
||||
)
|
||||
|
|
38
go.sum
38
go.sum
|
@ -1,10 +1,48 @@
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
|
||||
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
|
||||
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
|
||||
github.com/urfave/cli/v2 v2.25.1 h1:zw8dSP7ghX0Gmm8vugrs6q9Ku0wzweqPyshy+syu9Gw=
|
||||
github.com/urfave/cli/v2 v2.25.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
|
||||
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
@ -29,3 +29,8 @@ type CanSink interface {
|
|||
type CanSource interface {
|
||||
Recv() (*Frame, error)
|
||||
}
|
||||
|
||||
type CanTransciever interface {
|
||||
CanSink
|
||||
CanSource
|
||||
}
|
26
internal/can/frame_kind.go
Normal file
26
internal/can/frame_kind.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Code generated by "stringer -output=frame_kind.go -type Kind"; DO NOT EDIT.
|
||||
|
||||
package can
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[SFF-0]
|
||||
_ = x[EFF-1]
|
||||
_ = x[RTR-2]
|
||||
_ = x[ERR-3]
|
||||
}
|
||||
|
||||
const _Kind_name = "SFFEFFRTRERR"
|
||||
|
||||
var _Kind_index = [...]uint8{0, 3, 6, 9, 12}
|
||||
|
||||
func (i Kind) String() string {
|
||||
if i >= Kind(len(_Kind_index)-1) {
|
||||
return "Kind(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Kind_name[_Kind_index[i]:_Kind_index[i+1]]
|
||||
}
|
21
internal/db/can_adapter.go
Normal file
21
internal/db/can_adapter.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/kschamplin/gotelem/internal/can"
|
||||
)
|
||||
|
||||
// this file implements a CAN adapter for the sqlite db.
|
||||
|
||||
type CanDB struct {
|
||||
Db *sql.DB
|
||||
}
|
||||
|
||||
func (cdb *CanDB) Send(_ *can.Frame) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (cdb *CanDB) Recv() (*can.Frame, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
60
internal/gotelem/messages.go
Normal file
60
internal/gotelem/messages.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
package gotelem
|
||||
|
||||
import (
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
// a body is a thing that can get a type, which we put in the header.
|
||||
// we use the header to store metadata too
|
||||
type Body interface {
|
||||
GetType() string
|
||||
msgp.Marshaler
|
||||
}
|
||||
|
||||
//go:generate msgp
|
||||
type Data struct {
|
||||
Header map[string]string `msg:"header"`
|
||||
Body msgp.Raw `msg:"body"`
|
||||
}
|
||||
|
||||
type CanBody struct {
|
||||
Id uint32 `msg:"id"`
|
||||
Payload []byte `msg:"data"`
|
||||
Source string `msg:"src"`
|
||||
}
|
||||
|
||||
func (*CanBody) GetType() string {
|
||||
return "canp"
|
||||
}
|
||||
|
||||
// A status contains information about the running application.
|
||||
// mainly internal battery percentage.
|
||||
type StatusBody struct {
|
||||
BatteryPct float32 `msg:"batt"`
|
||||
ErrCode int16 `msg:"err"` // 0 is good.
|
||||
}
|
||||
|
||||
func (*StatusBody) GetType() string {
|
||||
return "status"
|
||||
}
|
||||
|
||||
// takes anything that has a GetType() string method and packs it up.
|
||||
func NewData(body Body) (*Data, error) {
|
||||
data := &Data{}
|
||||
|
||||
data.Header["type"] = body.GetType()
|
||||
|
||||
// add other metadata here.
|
||||
data.Header["ver"] = "0.0.1"
|
||||
|
||||
data.Header["test"] = "mesg"
|
||||
|
||||
rawBody, err := body.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data.Body = rawBody
|
||||
|
||||
return data, nil
|
||||
}
|
491
internal/gotelem/messages_gen.go
Normal file
491
internal/gotelem/messages_gen.go
Normal file
|
@ -0,0 +1,491 @@
|
|||
package gotelem
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *CanBody) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "id":
|
||||
z.Id, err = dc.ReadUint32()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Id")
|
||||
return
|
||||
}
|
||||
case "data":
|
||||
z.Payload, err = dc.ReadBytes(z.Payload)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Payload")
|
||||
return
|
||||
}
|
||||
case "src":
|
||||
z.Source, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Source")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *CanBody) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 3
|
||||
// write "id"
|
||||
err = en.Append(0x83, 0xa2, 0x69, 0x64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteUint32(z.Id)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Id")
|
||||
return
|
||||
}
|
||||
// write "data"
|
||||
err = en.Append(0xa4, 0x64, 0x61, 0x74, 0x61)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBytes(z.Payload)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Payload")
|
||||
return
|
||||
}
|
||||
// write "src"
|
||||
err = en.Append(0xa3, 0x73, 0x72, 0x63)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Source)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Source")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *CanBody) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 3
|
||||
// string "id"
|
||||
o = append(o, 0x83, 0xa2, 0x69, 0x64)
|
||||
o = msgp.AppendUint32(o, z.Id)
|
||||
// string "data"
|
||||
o = append(o, 0xa4, 0x64, 0x61, 0x74, 0x61)
|
||||
o = msgp.AppendBytes(o, z.Payload)
|
||||
// string "src"
|
||||
o = append(o, 0xa3, 0x73, 0x72, 0x63)
|
||||
o = msgp.AppendString(o, z.Source)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *CanBody) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "id":
|
||||
z.Id, bts, err = msgp.ReadUint32Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Id")
|
||||
return
|
||||
}
|
||||
case "data":
|
||||
z.Payload, bts, err = msgp.ReadBytesBytes(bts, z.Payload)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Payload")
|
||||
return
|
||||
}
|
||||
case "src":
|
||||
z.Source, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Source")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *CanBody) Msgsize() (s int) {
|
||||
s = 1 + 3 + msgp.Uint32Size + 5 + msgp.BytesPrefixSize + len(z.Payload) + 4 + msgp.StringPrefixSize + len(z.Source)
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *Data) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "header":
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Header")
|
||||
return
|
||||
}
|
||||
if z.Header == nil {
|
||||
z.Header = make(map[string]string, zb0002)
|
||||
} else if len(z.Header) > 0 {
|
||||
for key := range z.Header {
|
||||
delete(z.Header, key)
|
||||
}
|
||||
}
|
||||
for zb0002 > 0 {
|
||||
zb0002--
|
||||
var za0001 string
|
||||
var za0002 string
|
||||
za0001, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Header")
|
||||
return
|
||||
}
|
||||
za0002, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Header", za0001)
|
||||
return
|
||||
}
|
||||
z.Header[za0001] = za0002
|
||||
}
|
||||
case "body":
|
||||
err = z.Body.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Body")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *Data) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 2
|
||||
// write "header"
|
||||
err = en.Append(0x82, 0xa6, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteMapHeader(uint32(len(z.Header)))
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Header")
|
||||
return
|
||||
}
|
||||
for za0001, za0002 := range z.Header {
|
||||
err = en.WriteString(za0001)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Header")
|
||||
return
|
||||
}
|
||||
err = en.WriteString(za0002)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Header", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
// write "body"
|
||||
err = en.Append(0xa4, 0x62, 0x6f, 0x64, 0x79)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = z.Body.EncodeMsg(en)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Body")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *Data) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 2
|
||||
// string "header"
|
||||
o = append(o, 0x82, 0xa6, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72)
|
||||
o = msgp.AppendMapHeader(o, uint32(len(z.Header)))
|
||||
for za0001, za0002 := range z.Header {
|
||||
o = msgp.AppendString(o, za0001)
|
||||
o = msgp.AppendString(o, za0002)
|
||||
}
|
||||
// string "body"
|
||||
o = append(o, 0xa4, 0x62, 0x6f, 0x64, 0x79)
|
||||
o, err = z.Body.MarshalMsg(o)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Body")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *Data) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "header":
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Header")
|
||||
return
|
||||
}
|
||||
if z.Header == nil {
|
||||
z.Header = make(map[string]string, zb0002)
|
||||
} else if len(z.Header) > 0 {
|
||||
for key := range z.Header {
|
||||
delete(z.Header, key)
|
||||
}
|
||||
}
|
||||
for zb0002 > 0 {
|
||||
var za0001 string
|
||||
var za0002 string
|
||||
zb0002--
|
||||
za0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Header")
|
||||
return
|
||||
}
|
||||
za0002, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Header", za0001)
|
||||
return
|
||||
}
|
||||
z.Header[za0001] = za0002
|
||||
}
|
||||
case "body":
|
||||
bts, err = z.Body.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Body")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *Data) Msgsize() (s int) {
|
||||
s = 1 + 7 + msgp.MapHeaderSize
|
||||
if z.Header != nil {
|
||||
for za0001, za0002 := range z.Header {
|
||||
_ = za0002
|
||||
s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002)
|
||||
}
|
||||
}
|
||||
s += 5 + z.Body.Msgsize()
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *StatusBody) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "batt":
|
||||
z.BatteryPct, err = dc.ReadFloat32()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BatteryPct")
|
||||
return
|
||||
}
|
||||
case "err":
|
||||
z.ErrCode, err = dc.ReadInt16()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ErrCode")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z StatusBody) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 2
|
||||
// write "batt"
|
||||
err = en.Append(0x82, 0xa4, 0x62, 0x61, 0x74, 0x74)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteFloat32(z.BatteryPct)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BatteryPct")
|
||||
return
|
||||
}
|
||||
// write "err"
|
||||
err = en.Append(0xa3, 0x65, 0x72, 0x72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt16(z.ErrCode)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ErrCode")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z StatusBody) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 2
|
||||
// string "batt"
|
||||
o = append(o, 0x82, 0xa4, 0x62, 0x61, 0x74, 0x74)
|
||||
o = msgp.AppendFloat32(o, z.BatteryPct)
|
||||
// string "err"
|
||||
o = append(o, 0xa3, 0x65, 0x72, 0x72)
|
||||
o = msgp.AppendInt16(o, z.ErrCode)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *StatusBody) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "batt":
|
||||
z.BatteryPct, bts, err = msgp.ReadFloat32Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "BatteryPct")
|
||||
return
|
||||
}
|
||||
case "err":
|
||||
z.ErrCode, bts, err = msgp.ReadInt16Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "ErrCode")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z StatusBody) Msgsize() (s int) {
|
||||
s = 1 + 5 + msgp.Float32Size + 4 + msgp.Int16Size
|
||||
return
|
||||
}
|
349
internal/gotelem/messages_gen_test.go
Normal file
349
internal/gotelem/messages_gen_test.go
Normal file
|
@ -0,0 +1,349 @@
|
|||
package gotelem
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
func TestMarshalUnmarshalCanBody(t *testing.T) {
|
||||
v := CanBody{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsgCanBody(b *testing.B) {
|
||||
v := CanBody{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgCanBody(b *testing.B) {
|
||||
v := CanBody{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalCanBody(b *testing.B) {
|
||||
v := CanBody{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodeCanBody(t *testing.T) {
|
||||
v := CanBody{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodeCanBody Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := CanBody{}
|
||||
err := msgp.Decode(&buf, &vn)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
msgp.Encode(&buf, &v)
|
||||
err = msgp.NewReader(&buf).Skip()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeCanBody(b *testing.B) {
|
||||
v := CanBody{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
en := msgp.NewWriter(msgp.Nowhere)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.EncodeMsg(en)
|
||||
}
|
||||
en.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkDecodeCanBody(b *testing.B) {
|
||||
v := CanBody{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
rd := msgp.NewEndlessReader(buf.Bytes(), b)
|
||||
dc := msgp.NewReader(rd)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := v.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalData(t *testing.T) {
|
||||
v := Data{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsgData(b *testing.B) {
|
||||
v := Data{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgData(b *testing.B) {
|
||||
v := Data{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalData(b *testing.B) {
|
||||
v := Data{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodeData(t *testing.T) {
|
||||
v := Data{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodeData Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := Data{}
|
||||
err := msgp.Decode(&buf, &vn)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
msgp.Encode(&buf, &v)
|
||||
err = msgp.NewReader(&buf).Skip()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeData(b *testing.B) {
|
||||
v := Data{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
en := msgp.NewWriter(msgp.Nowhere)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.EncodeMsg(en)
|
||||
}
|
||||
en.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkDecodeData(b *testing.B) {
|
||||
v := Data{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
rd := msgp.NewEndlessReader(buf.Bytes(), b)
|
||||
dc := msgp.NewReader(rd)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := v.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalStatusBody(t *testing.T) {
|
||||
v := StatusBody{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsgStatusBody(b *testing.B) {
|
||||
v := StatusBody{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgStatusBody(b *testing.B) {
|
||||
v := StatusBody{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalStatusBody(b *testing.B) {
|
||||
v := StatusBody{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodeStatusBody(t *testing.T) {
|
||||
v := StatusBody{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodeStatusBody Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := StatusBody{}
|
||||
err := msgp.Decode(&buf, &vn)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
msgp.Encode(&buf, &v)
|
||||
err = msgp.NewReader(&buf).Skip()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeStatusBody(b *testing.B) {
|
||||
v := StatusBody{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
en := msgp.NewWriter(msgp.Nowhere)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.EncodeMsg(en)
|
||||
}
|
||||
en.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkDecodeStatusBody(b *testing.B) {
|
||||
v := StatusBody{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
rd := msgp.NewEndlessReader(buf.Bytes(), b)
|
||||
dc := msgp.NewReader(rd)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := v.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/kschamplin/gotelem/can"
|
||||
"github.com/kschamplin/gotelem/internal/can"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/kschamplin/gotelem/can"
|
||||
"github.com/kschamplin/gotelem/internal/can"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
235
internal/xbee/api_frame.go
Normal file
235
internal/xbee/api_frame.go
Normal file
|
@ -0,0 +1,235 @@
|
|||
package xbee
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// the frames have an outer shell - we will make a function that takes
|
||||
// an inner frame element and wraps it in the appropriate headers.
|
||||
|
||||
// first, we should make it take the frame directly, so we make an interface
|
||||
// that represents "framable" things. note that bytes.Buffer also fulfils this.
|
||||
|
||||
type Frameable interface {
|
||||
// returns the API identifier for this frame.
|
||||
GetId() byte
|
||||
// encodes this frame correctly.
|
||||
Bytes() ([]byte, error)
|
||||
}
|
||||
|
||||
// now we can describe our function that takes a framable and contains it + calculates checksums.
|
||||
func calculateChecksum(data []byte) byte {
|
||||
var sum byte
|
||||
for _, v := range data {
|
||||
sum += v
|
||||
}
|
||||
return 0xFF - sum
|
||||
}
|
||||
|
||||
func WriteFrame(w io.Writer, cmd Frameable) (n int, err error) {
|
||||
frame_data, err := cmd.Bytes()
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
frame := make([]byte, len(frame_data)+4)
|
||||
frame[0] = 0x7E
|
||||
|
||||
binary.BigEndian.PutUint16(frame[1:], uint16(len(frame_data)))
|
||||
|
||||
copy(frame[3:], frame_data)
|
||||
|
||||
chk := calculateChecksum(frame_data)
|
||||
|
||||
frame[len(frame)-1] = chk
|
||||
return w.Write(frame)
|
||||
}
|
||||
|
||||
func makeXbeeApiFrame(cmd Frameable) ([]byte, error) {
|
||||
dataBuf, _ := cmd.Bytes()
|
||||
frameBuf := make([]byte, len(dataBuf)+4)
|
||||
|
||||
// move data and construct the frame
|
||||
|
||||
frameBuf[0] = 0x7E // start delimiter
|
||||
|
||||
// length
|
||||
// todo: check endiannes (0x7e, msb lsb)
|
||||
binary.BigEndian.PutUint16(frameBuf[1:3], uint16(len(dataBuf)))
|
||||
|
||||
copy(frameBuf[3:], dataBuf)
|
||||
|
||||
chksum := calculateChecksum(dataBuf)
|
||||
|
||||
frameBuf[len(frameBuf)-1] = chksum
|
||||
|
||||
return frameBuf, nil
|
||||
}
|
||||
|
||||
// now we can describe frames in other files that implement Frameable.
|
||||
// the remaining challenge is reception and actual API frames.
|
||||
// xbee uses the first byte of the "frame data" as the API identifier or command.
|
||||
|
||||
//go:generate stringer -output=api_frame_cmd.go -type xbeeCmd
|
||||
type XBeeCmd byte
|
||||
|
||||
const (
|
||||
// commands sent to the xbee s3b
|
||||
|
||||
ATCmd XBeeCmd = 0x08 // AT Command
|
||||
ATCmdQueue XBeeCmd = 0x09 // AT Command - Queue Parameter Value
|
||||
TxReq XBeeCmd = 0x10 // TX Request
|
||||
TxReqExpl XBeeCmd = 0x11 // Explicit TX Request
|
||||
RemoteCmdReq XBeeCmd = 0x17 // Remote Command Request
|
||||
// commands recieved from the xbee
|
||||
|
||||
ATCmdResponse XBeeCmd = 0x88 // AT Command Response
|
||||
ModemStatus XBeeCmd = 0x8A // Modem Status
|
||||
TxStatus XBeeCmd = 0x8B // Transmit Status
|
||||
RouteInfoPkt XBeeCmd = 0x8D // Route information packet
|
||||
AddrUpdate XBeeCmd = 0x8E // Aggregate Addressing Update
|
||||
RxPkt XBeeCmd = 0x90 // RX Indicator (AO=0)
|
||||
RxPktExpl XBeeCmd = 0x91 // Explicit RX Indicator (AO=1)
|
||||
IOSample XBeeCmd = 0x92 // Data Sample RX Indicator
|
||||
NodeId XBeeCmd = 0x95 // Note Identification Indicator
|
||||
RemoteCmdResp XBeeCmd = 0x97 // Remote Command Response
|
||||
)
|
||||
|
||||
// AT commands are hard, so let's write out all the major ones here
|
||||
|
||||
type ATCmdFrame struct {
|
||||
Id byte
|
||||
Cmd string
|
||||
Param []byte
|
||||
Queued bool
|
||||
}
|
||||
|
||||
// implement the frame stuff for us.
|
||||
func (atFrame *ATCmdFrame) Bytes() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if atFrame.Queued {
|
||||
// queued (batched) at comamnds have different Frame type
|
||||
buf.WriteByte(byte(ATCmdQueue))
|
||||
|
||||
} else {
|
||||
// normal frame type
|
||||
buf.WriteByte(byte(ATCmd))
|
||||
|
||||
}
|
||||
|
||||
buf.WriteByte(atFrame.Id)
|
||||
|
||||
// write cmd, if it's the right length.
|
||||
if cmdLen := len(atFrame.Cmd); cmdLen != 2 {
|
||||
return nil, fmt.Errorf("AT command incorrect length: %d", cmdLen)
|
||||
}
|
||||
buf.Write([]byte(atFrame.Cmd))
|
||||
|
||||
// write param.
|
||||
buf.Write(atFrame.Param)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// transmissions to this address are instead broadcast
|
||||
const BroadcastAddr = 0xFFFF
|
||||
|
||||
type TxFrame struct {
|
||||
Id byte
|
||||
Destination uint64
|
||||
BCastRadius uint8
|
||||
Options uint8
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (txFrame *TxFrame) Bytes() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
buf.WriteByte(byte(TxReq))
|
||||
|
||||
buf.WriteByte(txFrame.Id)
|
||||
|
||||
a := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(a, txFrame.Destination)
|
||||
buf.Write(a)
|
||||
|
||||
// write the reserved part.
|
||||
buf.Write([]byte{0xFF, 0xFE})
|
||||
|
||||
// write the radius
|
||||
buf.WriteByte(txFrame.BCastRadius)
|
||||
|
||||
buf.WriteByte(txFrame.Options)
|
||||
|
||||
buf.Write(txFrame.Payload)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type RemoteATCmdReq struct {
|
||||
ATCmdFrame
|
||||
Destination uint64
|
||||
Options uint8
|
||||
}
|
||||
|
||||
func (remoteAT *RemoteATCmdReq) Bytes() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.WriteByte(byte(RemoteCmdReq))
|
||||
|
||||
buf.WriteByte(remoteAT.Id)
|
||||
|
||||
a := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(a, remoteAT.Destination)
|
||||
buf.Write(a)
|
||||
|
||||
// write the reserved part.
|
||||
buf.Write([]byte{0xFF, 0xFE})
|
||||
// write options
|
||||
buf.WriteByte(remoteAT.Options)
|
||||
|
||||
// now, write the AT command and the data.
|
||||
buf.Write([]byte(remoteAT.Cmd))
|
||||
|
||||
buf.Write(remoteAT.Param)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
// this is a split function for bufio.scanner. It makes it easier to handle the FSM
|
||||
// for extracting data from a stream. For the Xbee, this means that we must
|
||||
// find the magic start character, (check that it's escaped), read the length,
|
||||
// and then ensure we have enough length to finish the token, requesting more data
|
||||
// if we do not.
|
||||
//
|
||||
// see https://pkg.go.dev/bufio#SplitFunc for more info
|
||||
// https://medium.com/golangspec/in-depth-introduction-to-bufio-scanner-in-golang-55483bb689b4
|
||||
func xbeeFrameSplit(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||
if atEOF && len(data) == 0 {
|
||||
// there's no data, request more.
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
if startIdx := bytes.IndexByte(data, 0x7E); startIdx >= 0 {
|
||||
// we have a start character. get the length.
|
||||
// we add 4 since start delimiter (1) + length (2) + checksum (1).
|
||||
// the length inside the packet represents the frame data only.
|
||||
var frameLen = binary.BigEndian.Uint16(data[startIdx+1:startIdx+3]) + 4
|
||||
if len(data[startIdx:]) < int(frameLen) {
|
||||
// we got the length, but there's not enough data for the frame. we can trim the
|
||||
// data that came before the start, but not return a token.
|
||||
return startIdx, nil, nil
|
||||
}
|
||||
// there is enough data to pull a frame.
|
||||
// todo: check checksum here? we can return an error.
|
||||
return startIdx + int(frameLen), data[startIdx : startIdx+int(frameLen)], nil
|
||||
}
|
||||
// we didn't find a start character in our data, so request more. trash everythign given to us
|
||||
return len(data), nil, nil
|
||||
}
|
119
internal/xbee/api_frame_test.go
Normal file
119
internal/xbee/api_frame_test.go
Normal file
|
@ -0,0 +1,119 @@
|
|||
package xbee
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_xbeeFrameSplit(t *testing.T) {
|
||||
type args struct {
|
||||
data []byte
|
||||
atEOF bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantAdvance int
|
||||
wantToken []byte
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
{
|
||||
name: "empty data",
|
||||
args: args{
|
||||
data: []byte{},
|
||||
atEOF: false,
|
||||
},
|
||||
wantAdvance: 0,
|
||||
wantToken: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no start delimiter",
|
||||
args: args{
|
||||
data: []byte{0x11, 0x22, 0x23, 0x44, 0x44, 0x77, 0x33},
|
||||
atEOF: false,
|
||||
},
|
||||
wantAdvance: 7,
|
||||
wantToken: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "incomplete packet",
|
||||
args: args{
|
||||
data: []byte{0x7E, 0x00, 0x02, 0x23, 0x11},
|
||||
atEOF: false,
|
||||
},
|
||||
wantAdvance: 0,
|
||||
wantToken: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid packet",
|
||||
args: args{
|
||||
data: []byte{0x7E, 0x00, 0x02, 0x23, 0x11, 0xCB},
|
||||
atEOF: false,
|
||||
},
|
||||
wantAdvance: 6,
|
||||
wantToken: []byte{0x7E, 0x00, 0x02, 0x23, 0x11, 0xCB},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid packet w/ padding",
|
||||
args: args{
|
||||
data: []byte{0x00, 0x7E, 0x00, 0x02, 0x23, 0x11, 0xCB, 0x00},
|
||||
atEOF: false,
|
||||
},
|
||||
wantAdvance: 7,
|
||||
wantToken: []byte{0x7E, 0x00, 0x02, 0x23, 0x11, 0xCB},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotAdvance, gotToken, err := xbeeFrameSplit(tt.args.data, tt.args.atEOF)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("xbeeFrameSplit() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if gotAdvance != tt.wantAdvance {
|
||||
t.Errorf("xbeeFrameSplit() gotAdvance = %v, want %v", gotAdvance, tt.wantAdvance)
|
||||
}
|
||||
if !reflect.DeepEqual(gotToken, tt.wantToken) {
|
||||
t.Errorf("xbeeFrameSplit() gotToken = %v, want %v", gotToken, tt.wantToken)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteFrame(t *testing.T) {
|
||||
type args struct {
|
||||
cmd Frameable
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantN int
|
||||
wantW string
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := &bytes.Buffer{}
|
||||
gotN, err := WriteFrame(w, tt.args.cmd)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("WriteFrame() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if gotN != tt.wantN {
|
||||
t.Errorf("WriteFrame() = %v, want %v", gotN, tt.wantN)
|
||||
}
|
||||
if gotW := w.String(); gotW != tt.wantW {
|
||||
t.Errorf("WriteFrame() = %v, want %v", gotW, tt.wantW)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
package xbee
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// the frames have an outer shell - we will make a function that takes
|
||||
// an inner frame element and wraps it in the appropriate headers.
|
||||
|
||||
// first, we should make it take the frame directly, so we make an interface
|
||||
// that represents "framable" things. note that bytes.Buffer also fulfils this.
|
||||
|
||||
type Frameable interface {
|
||||
Bytes() []byte
|
||||
}
|
||||
|
||||
// now we can describe our function that takes a framable and contains it + calculates checksums.
|
||||
func calculateChecksum(data []byte) byte {
|
||||
var sum byte
|
||||
for _, v := range data {
|
||||
sum += v
|
||||
}
|
||||
return 0xFF - sum
|
||||
}
|
||||
func makeXbeeApiFrame(cmd Frameable) ([]byte, error) {
|
||||
dataBuf := cmd.Bytes()
|
||||
frameBuf := make([]byte, len(dataBuf)+4)
|
||||
|
||||
// move data and construct the frame
|
||||
|
||||
frameBuf[0] = 0x7E // start delimiter
|
||||
|
||||
// length
|
||||
// todo: check endiannes (0x7e, msb lsb)
|
||||
binary.LittleEndian.PutUint16(frameBuf[1:3], uint16(len(dataBuf)))
|
||||
|
||||
copy(frameBuf[3:], dataBuf)
|
||||
|
||||
chksum := calculateChecksum(dataBuf)
|
||||
|
||||
frameBuf[len(frameBuf)-1] = chksum
|
||||
|
||||
return frameBuf, nil
|
||||
}
|
||||
|
||||
// now we can describe frames in other files that implement Frameable. this makes trasmission complete.
|
||||
// the remaining challenge is reception and actual API frames.
|
||||
// xbee uses the first byte of the "frame data" as the API identifier or command.
|
||||
|
||||
//go:generate stringer -output=api_frame_cmd.go -type xbeeCmd
|
||||
type xbeeCmd byte
|
||||
|
||||
const (
|
||||
// commands sent to the xbee s3b
|
||||
|
||||
ATCmd xbeeCmd = 0x08 // AT Command
|
||||
ATCmdQueuePVal xbeeCmd = 0x09 // AT Command - Queue Parameter Value
|
||||
TxReq xbeeCmd = 0x10 // TX Request
|
||||
TxReqExpl xbeeCmd = 0x11 // Explicit TX Request
|
||||
RemoteCmdReq xbeeCmd = 0x17 // Remote Command Request
|
||||
// commands recieved from the xbee
|
||||
|
||||
ATCmdResponse xbeeCmd = 0x88 // AT Command Response
|
||||
ModemStatus xbeeCmd = 0x8A // Modem Status
|
||||
TxStatus xbeeCmd = 0x8B // Transmit Status
|
||||
RouteInfoPkt xbeeCmd = 0x8D // Route information packet
|
||||
AddrUpdate xbeeCmd = 0x8E // Aggregate Addressing Update
|
||||
RxPkt xbeeCmd = 0x90 // RX Indicator (AO=0)
|
||||
RxPktExpl xbeeCmd = 0x91 // Explicit RX Indicator (AO=1)
|
||||
IOSample xbeeCmd = 0x92 // Data Sample RX Indicator
|
||||
NodeId xbeeCmd = 0x95 // Note Identification Indicator
|
||||
RemoteCmdResp xbeeCmd = 0x97 // Remote Command Response
|
||||
)
|
|
@ -1 +0,0 @@
|
|||
package xbee_test
|
Loading…
Reference in a new issue