cleanup docs, tests, housekeeping
This commit is contained in:
parent
aa5cd0b393
commit
59760b79dd
|
@ -14,6 +14,12 @@ type Frame struct {
|
|||
Kind Kind
|
||||
}
|
||||
|
||||
type CANFrame interface {
|
||||
Id() uint32
|
||||
Data() []byte
|
||||
Type() Kind
|
||||
}
|
||||
|
||||
//go:generate stringer -output=frame_kind.go -type Kind
|
||||
|
||||
// Kind is the type of the can Frame
|
||||
|
|
|
@ -20,10 +20,13 @@ Notify() the callback service with the new event as an argument every time it
|
|||
occured. While this may be less optimal than protocol-level streaming, it is
|
||||
far simpler.
|
||||
|
||||
# Generic Helper Functions
|
||||
|
||||
The idiomatic way to use mprpc is to use the generic functions that are provided
|
||||
as helpers. They allow the programmer to easily wrap functions in a closure that
|
||||
automatically encodes and decodes the parameters and results to their
|
||||
MessagePack representations. See the Make* generic functions for more information.
|
||||
as helpers. They allow the programmer to easily wrap existing functions in a
|
||||
closure that automatically encodes and decodes the parameters and results to
|
||||
their MessagePack representations. See the Make* generic functions for more
|
||||
information.
|
||||
|
||||
// Assume myParam and myResult are MessagePack-enabled structs.
|
||||
// Use `msgp` to generate the required functions for them.
|
||||
|
@ -37,6 +40,9 @@ MessagePack representations. See the Make* generic functions for more informatio
|
|||
The generic functions allow for flexiblity and elegant code while still keeping
|
||||
the underlying implementation reflect-free. For more complex functions (i.e
|
||||
multiple parameters or return types), a second layer of indirection can be used.
|
||||
|
||||
There is also a `MakeCaller` function that can make a stub function that handles
|
||||
encoding the arguments and decoding the response for a remote procedure.
|
||||
*/
|
||||
package mprpc
|
||||
|
||||
|
@ -155,6 +161,7 @@ func (rpc *RPCConn) Serve() {
|
|||
}
|
||||
}
|
||||
|
||||
// dispatch is an internal method used to execute a Request sent by the remote:w
|
||||
func (rpc *RPCConn) dispatch(req Request) {
|
||||
|
||||
result, err := rpc.handlers[req.Method](req.Params)
|
||||
|
@ -172,6 +179,9 @@ func (rpc *RPCConn) dispatch(req Request) {
|
|||
response.EncodeMsg(w)
|
||||
|
||||
}
|
||||
|
||||
// dispatchNotif is like dispatch, but for Notifications. This means that it never replies,
|
||||
// even if there is an error.
|
||||
func (rpc *RPCConn) dispatchNotif(req Notification) {
|
||||
|
||||
_, err := rpc.handlers[req.Method](req.Params)
|
||||
|
@ -201,7 +211,7 @@ func MakeService[T, R msgpackObject](fn func(T) (R, error)) ServiceFunc {
|
|||
return func(p msgp.Raw) (msgp.Raw, error) {
|
||||
// decode the raw data into a new underlying type.
|
||||
var params T
|
||||
// TODO: handler errors
|
||||
|
||||
_, err := params.UnmarshalMsg(p)
|
||||
|
||||
if err != nil {
|
||||
|
@ -236,8 +246,13 @@ func MakeService[T, R msgpackObject](fn func(T) (R, error)) ServiceFunc {
|
|||
func MakeCaller[T, R msgpackObject]() func(string, T, *RPCConn) (R, error) {
|
||||
return func(method string, param T, rpc *RPCConn) (R, error) {
|
||||
|
||||
rawParam, _ := param.MarshalMsg([]byte{})
|
||||
rawParam, err := param.MarshalMsg([]byte{})
|
||||
if err != nil {
|
||||
var emtpyR R
|
||||
return emtpyR, err
|
||||
}
|
||||
rawResponse, err := rpc.Call(method, rawParam)
|
||||
|
||||
if err != nil {
|
||||
var emtpyR R
|
||||
return emtpyR, err
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*
|
||||
socketcan provides a wrapper around the Linux socketCAN interface.
|
||||
*/
|
||||
package socketcan
|
||||
|
||||
import (
|
||||
|
@ -18,12 +21,13 @@ type CanSocket struct {
|
|||
fd int
|
||||
}
|
||||
|
||||
// standardFrameSize is the full size in bytes of the default CAN frame.
|
||||
const standardFrameSize = unix.CAN_MTU
|
||||
|
||||
// we use the base CAN_MTU since the FD MTU is not in sys/unix. but we know it's +64-8 bytes
|
||||
const fdFrameSize = unix.CAN_MTU + 56
|
||||
|
||||
// Constructs a new CanSocket and binds it to the interface given by ifname
|
||||
// NewCanSocket constructs a new CanSocket and binds it to the interface given by ifname
|
||||
func NewCanSocket(ifname string) (*CanSocket, error) {
|
||||
|
||||
var sck CanSocket
|
||||
|
@ -52,17 +56,17 @@ func NewCanSocket(ifname string) (*CanSocket, error) {
|
|||
return &sck, nil
|
||||
}
|
||||
|
||||
// Closes the socket.
|
||||
// Close closes the socket.
|
||||
func (sck *CanSocket) Close() error {
|
||||
return unix.Close(sck.fd)
|
||||
}
|
||||
|
||||
// get the name of the socket.
|
||||
// Name returns the name of the socket.
|
||||
func (sck *CanSocket) Name() string {
|
||||
return sck.iface.Name
|
||||
}
|
||||
|
||||
// Sets if error packets should be sent upstream
|
||||
// SetErrFilter sets if error packets should be sent upstream
|
||||
func (sck *CanSocket) SetErrFilter(shouldFilter bool) error {
|
||||
|
||||
var err error
|
||||
|
@ -114,6 +118,7 @@ func (sck *CanSocket) SetFilters(filters []can.CanFilter) error {
|
|||
|
||||
}
|
||||
|
||||
// Send sends a CAN frame
|
||||
func (sck *CanSocket) Send(msg *can.Frame) error {
|
||||
|
||||
buf := make([]byte, fdFrameSize)
|
||||
|
|
|
@ -6,35 +6,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/kschamplin/gotelem/internal/can"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func TestMakeFilter(t *testing.T) {
|
||||
|
||||
t.Run("non-invert", func(t *testing.T) {
|
||||
|
||||
filter := can.CanFilter{Id: 0x123, Mask: 0x11, Inverted: true}
|
||||
|
||||
if filter.Id != 0x123 {
|
||||
t.Errorf("expected %d, got %d", 0x123, filter.Id)
|
||||
}
|
||||
if filter.Mask != 0x11 {
|
||||
t.Errorf("expected %d, got %d", 0x11, filter.Mask)
|
||||
}
|
||||
})
|
||||
t.Run("invert", func(t *testing.T) {
|
||||
|
||||
filter := can.CanFilter{Id: 0x123, Mask: 0x11, Inverted: true}
|
||||
|
||||
if filter.Id != 0x123|unix.CAN_INV_FILTER {
|
||||
t.Errorf("expected %d, got %d", 0x123|unix.CAN_INV_FILTER, filter.Id)
|
||||
}
|
||||
if filter.Mask != 0x11 {
|
||||
t.Errorf("expected %d, got %d", 0x11, filter.Mask)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCanSocket(t *testing.T) {
|
||||
|
||||
if _, err := net.InterfaceByName("vcan0"); err != nil {
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
// 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.
|
||||
|
||||
// Frameable is an object that can be sent in an XBee Frame. An XBee Frame
|
||||
// consists of a start delimiter, length, the payload, and a checksum.
|
||||
type Frameable interface {
|
||||
// returns the API identifier for this frame.
|
||||
// encodes this frame correctly.
|
||||
|
|
|
@ -36,7 +36,7 @@ func (b RawATCmd) Bytes() []byte {
|
|||
// EncodeATCommand takes an AT command and encodes it in the payload format.
|
||||
// it takes the frame index (which can be zero) as well as if it should be queued or
|
||||
// not. It encodes the AT command to be framed and sent over the wire and returns the packet
|
||||
func encodeATCommand(at ATCmd, idx uint8, queued bool) RawATCmd {
|
||||
func encodeATCommand(cmd [2]rune, p []byte, idx uint8, queued bool) RawATCmd {
|
||||
// we encode a new byte slice that contains the cmd + payload concatenated correclty.
|
||||
// this is then used to make the command frame, which contains ID/Type/Queued or not.
|
||||
// the ATCmdFrame can be converted to bytes to be sent over the wire once framed.
|
||||
|
@ -53,11 +53,9 @@ func encodeATCommand(at ATCmd, idx uint8, queued bool) RawATCmd {
|
|||
// next is the provided frame identifier, used for callbacks.
|
||||
buf.WriteByte(idx)
|
||||
|
||||
cmd := at.Cmd()
|
||||
buf.WriteByte(byte(cmd[0]))
|
||||
buf.WriteByte(byte(cmd[1]))
|
||||
// the payload can be empty. This would make it a query.
|
||||
p := at.Payload()
|
||||
buf.Write(p)
|
||||
|
||||
return buf.Bytes()
|
||||
|
@ -139,22 +137,3 @@ func encodeRemoteATCommand(at ATCmd, idx uint8, queued bool, destination uint64)
|
|||
// let's actually define some AT commands now.
|
||||
|
||||
// TODO: should we just use a function.
|
||||
// the AT command for the ID (Network ID).
|
||||
// the network identifier is used to communicate with other devices. It must match.
|
||||
type ATCmdID struct {
|
||||
id uint32
|
||||
isQuery bool
|
||||
}
|
||||
|
||||
func (cmd ATCmdID) Cmd() [2]rune {
|
||||
return [2]rune{'I', 'D'}
|
||||
}
|
||||
|
||||
func (cmd ATCmdID) Payload() []byte {
|
||||
if cmd.isQuery {
|
||||
return []byte{}
|
||||
}
|
||||
res := make([]byte, 0)
|
||||
res = binary.BigEndian.AppendUint32(res, cmd.id)
|
||||
return res
|
||||
}
|
||||
|
|
108
internal/xbee/at_test.go
Normal file
108
internal/xbee/at_test.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
package xbee
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseATCmdResponse(t *testing.T) {
|
||||
type args struct {
|
||||
p []byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *ATCmdResponse
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseATCmdResponse(tt.args.p)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ParseATCmdResponse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ParseATCmdResponse() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_encodeRemoteATCommand(t *testing.T) {
|
||||
type args struct {
|
||||
at ATCmd
|
||||
idx uint8
|
||||
queued bool
|
||||
destination uint64
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want RawATCmd
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := encodeRemoteATCommand(tt.args.at, tt.args.idx, tt.args.queued, tt.args.destination); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("encodeRemoteATCommand() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_encodeATCommand(t *testing.T) {
|
||||
type args struct {
|
||||
cmd [2]rune
|
||||
p []byte
|
||||
idx uint8
|
||||
queued bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want RawATCmd
|
||||
}{
|
||||
// These test cases are from digi's documentation on the 900HP/XSC modules.
|
||||
{
|
||||
name: "Setting AT Command",
|
||||
args: args{
|
||||
cmd: [2]rune{'N', 'I'},
|
||||
idx: 0xA1,
|
||||
p: []byte{0x45, 0x6E, 0x64, 0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65},
|
||||
queued: false,
|
||||
},
|
||||
want: []byte{0x08, 0xA1, 0x4E, 0x49, 0x45, 0x6E, 0x64, 0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65},
|
||||
},
|
||||
{
|
||||
name: "Query AT Command",
|
||||
args: args{
|
||||
cmd: [2]rune{'T', 'P'},
|
||||
idx: 0x17,
|
||||
p: nil,
|
||||
queued: false,
|
||||
},
|
||||
want: []byte{0x08, 0x17, 0x54, 0x50},
|
||||
},
|
||||
{
|
||||
name: "Queue Local AT Command",
|
||||
args: args{
|
||||
cmd: [2]rune{'B', 'D'},
|
||||
idx: 0x53,
|
||||
p: []byte{0x07},
|
||||
queued: true,
|
||||
},
|
||||
want: []byte{0x09, 0x53, 0x42, 0x44, 0x07},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := encodeATCommand(tt.args.cmd, tt.args.p, tt.args.idx, tt.args.queued); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("encodeATCommand() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue