major cleanup/refactor
delete mprpc move can frame to it's own library create CANID type (better extended id support) rework database format, only uses name + json now busEvent and rawJsonEvent don't store the Id anymore
This commit is contained in:
parent
1812807581
commit
c4bdf122a8
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/kschamplin/gotelem"
|
||||
"github.com/kschamplin/gotelem/mprpc"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
@ -35,8 +34,3 @@ func client(ctx *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// the client should connect to a TCP server and listen to packets.
|
||||
func CANFrameHandler(f *gotelem.Frame) (*mprpc.RPCEmpty, error) {
|
||||
fmt.Printf("got frame, %v\n", f)
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -8,11 +8,13 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/kschamplin/gotelem/internal/can"
|
||||
"github.com/kschamplin/gotelem/skylab"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/exp/slog"
|
||||
|
@ -50,15 +52,164 @@ required for piping candump into skylabify. Likewise, data should be stored with
|
|||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "format",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "the format of the incoming data. One of 'telem', 'candump'",
|
||||
},
|
||||
}
|
||||
|
||||
app.Action = run
|
||||
app.HideHelp = true
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// A FormatError is an error when parsing a format. Typically we simply ignore
|
||||
// these and move on, but they can optionally wrap another error that is fatal.
|
||||
type FormatError struct {
|
||||
msg string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *FormatError) Error() string {
|
||||
return fmt.Sprintf("%s:%s", e.msg, e.err.Error())
|
||||
}
|
||||
func (e *FormatError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func NewFormatError(msg string, err error) error {
|
||||
return &FormatError{msg: msg, err: err}
|
||||
}
|
||||
|
||||
// A Parser takes a string containing one line of a particular log file
|
||||
// and returns an associated skylab.BusEvent representing the packet.
|
||||
// if no packet is found, an error is returned instead.
|
||||
type ParserFunc func(string) (skylab.BusEvent, error)
|
||||
|
||||
func parseCanDumpLine(dumpLine string) (b skylab.BusEvent, err error) {
|
||||
// dumpline looks like this:
|
||||
// (1684538768.521889) can0 200#8D643546
|
||||
// remove trailing newline
|
||||
dumpLine = strings.TrimSpace(dumpLine)
|
||||
segments := strings.Split(dumpLine, " ")
|
||||
|
||||
var unixSeconds, unixMicros int64
|
||||
fmt.Sscanf(segments[0], "(%d.%d)", &unixSeconds, &unixMicros)
|
||||
b.Timestamp = time.Unix(unixSeconds, unixMicros)
|
||||
|
||||
// now we extract the remaining data:
|
||||
hexes := strings.Split(segments[2], "#") // first portion is id, second is data
|
||||
|
||||
id, err := strconv.ParseUint(hexes[0], 16, 64)
|
||||
if err != nil {
|
||||
err = NewFormatError("failed to parse id", err)
|
||||
return
|
||||
}
|
||||
if (len(hexes[1]) % 2) != 0 {
|
||||
err = NewFormatError("odd number of hex characters", nil)
|
||||
return
|
||||
}
|
||||
rawData, err := hex.DecodeString(hexes[1])
|
||||
if err != nil {
|
||||
err = NewFormatError("failed to decode hex data", err)
|
||||
return
|
||||
}
|
||||
|
||||
frame := can.Frame{
|
||||
// TODO: fix extended ids. we assume not extended for now.
|
||||
Id: can.CanID{Id: uint32(id), Extended: false},
|
||||
Data: rawData,
|
||||
Kind: can.CanDataFrame,
|
||||
}
|
||||
|
||||
b.Data, err = skylab.FromCanFrame(frame)
|
||||
|
||||
if err != nil {
|
||||
err = NewFormatError("failed to parse can frame", err)
|
||||
return
|
||||
}
|
||||
|
||||
// set the name
|
||||
b.Name = b.Data.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func parseTelemLogLine(line string) (b skylab.BusEvent, err error) {
|
||||
// strip trailng newline since we rely on it being gone
|
||||
line = strings.TrimSpace(line)
|
||||
// data is of the form
|
||||
// 1698180835.318 0619D80564080EBE241
|
||||
// the second part there is 3 nibbles (12 bits, 3 hex chars) for can ID,
|
||||
// the rest is data.
|
||||
// this regex does the processing.
|
||||
r := regexp.MustCompile(`^(\d+).(\d{3}) (\w{3})(\w+)$`)
|
||||
|
||||
// these files tend to get corrupted. there are all kinds of nasties that can happen.
|
||||
// defense against random panics
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = NewFormatError("caught panic", nil)
|
||||
}
|
||||
}()
|
||||
a := r.FindStringSubmatch(line)
|
||||
if a == nil {
|
||||
err = NewFormatError("no regex match", nil)
|
||||
return
|
||||
}
|
||||
var unixSeconds, unixMillis int64
|
||||
// note that a contains 5 elements, the first being the full match.
|
||||
// so we start from the second element
|
||||
unixSeconds, err = strconv.ParseInt(a[1], 10, 0)
|
||||
if err != nil {
|
||||
err = NewFormatError("failed to parse unix seconds", err)
|
||||
return
|
||||
}
|
||||
unixMillis, err = strconv.ParseInt(a[2], 10, 0)
|
||||
if err != nil {
|
||||
err = NewFormatError("failed to parse unix millis", err)
|
||||
return
|
||||
}
|
||||
ts := time.Unix(unixSeconds, unixMillis*1e6)
|
||||
|
||||
id, err := strconv.ParseUint(a[3], 16, 16)
|
||||
if err != nil {
|
||||
err = NewFormatError("failed to parse id", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(a[4])%2 != 0 {
|
||||
// odd hex chars, protect against a panic
|
||||
err = NewFormatError("wrong amount of hex chars", nil)
|
||||
}
|
||||
rawData, err := hex.DecodeString(a[4])
|
||||
if err != nil {
|
||||
err = NewFormatError("failed to parse hex data", err)
|
||||
return
|
||||
}
|
||||
frame := can.Frame{
|
||||
Id: can.CanID{Id: uint32(id), Extended: false},
|
||||
Data: rawData,
|
||||
Kind: can.CanDataFrame,
|
||||
}
|
||||
b.Timestamp = ts
|
||||
b.Data, err = skylab.FromCanFrame(frame)
|
||||
if err != nil {
|
||||
err = NewFormatError("failed to parse can frame", err)
|
||||
return
|
||||
}
|
||||
b.Name = b.Data.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var parseMap = map[string]ParserFunc{
|
||||
"telem": parseTelemLogLine,
|
||||
"candump": parseCanDumpLine,
|
||||
}
|
||||
|
||||
func run(ctx *cli.Context) (err error) {
|
||||
path := ctx.Args().Get(0)
|
||||
if path == "" {
|
||||
|
@ -66,7 +217,6 @@ func run(ctx *cli.Context) (err error) {
|
|||
cli.ShowAppHelpAndExit(ctx, int(syscall.EINVAL))
|
||||
}
|
||||
|
||||
|
||||
var istream *os.File
|
||||
if path == "-" {
|
||||
istream = os.Stdin
|
||||
|
@ -77,57 +227,43 @@ func run(ctx *cli.Context) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
canDumpReader := bufio.NewReader(istream)
|
||||
fileReader := bufio.NewReader(istream)
|
||||
|
||||
var pfun ParserFunc
|
||||
|
||||
pfun, ok := parseMap[ctx.String("format")]
|
||||
if !ok {
|
||||
fmt.Println("invalid format!")
|
||||
cli.ShowAppHelpAndExit(ctx, int(syscall.EINVAL))
|
||||
}
|
||||
|
||||
n_err := 0
|
||||
unknown_packets := 0
|
||||
|
||||
for {
|
||||
// dumpline looks like this:
|
||||
// (1684538768.521889) can0 200#8D643546
|
||||
dumpLine, err := canDumpReader.ReadString('\n')
|
||||
line, err := fileReader.ReadString('\n')
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
return err // i/o failures are fatal
|
||||
}
|
||||
// remove trailing newline
|
||||
dumpLine = strings.TrimSpace(dumpLine)
|
||||
|
||||
segments := strings.Split(dumpLine, " ")
|
||||
|
||||
var cd skylab.BusEvent
|
||||
var unixSeconds, unixMicros int64
|
||||
fmt.Sscanf(segments[0], "(%d.%d)", &unixSeconds, &unixMicros)
|
||||
cd.Timestamp = time.Unix(unixSeconds, unixMicros*1000) // the canlog does usec precision for the decimal part.
|
||||
|
||||
// this is for the latter part, we need to split id/data
|
||||
hexes := strings.Split(segments[2], "#")
|
||||
|
||||
// get the id
|
||||
id, err := strconv.ParseUint(hexes[0], 16, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cd.Id = uint32(id)
|
||||
|
||||
// get the data to a []byte
|
||||
rawData, err := hex.DecodeString(hexes[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse the data []byte to a skylab packet
|
||||
cd.Data, err = skylab.FromCanFrame(uint32(cd.Id), rawData)
|
||||
f, err := pfun(line)
|
||||
var idErr *skylab.UnknownIdError
|
||||
if errors.As(err, &idErr) {
|
||||
// unknown id
|
||||
slog.Info("unknown id", "err", err)
|
||||
unknown_packets++
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
// TODO: we should consider absorbing all errors.
|
||||
fmt.Printf("got an error %v\n", err)
|
||||
n_err++
|
||||
continue
|
||||
}
|
||||
|
||||
// format and print out the JSON.
|
||||
out, _ := json.Marshal(&cd)
|
||||
out, _ := json.Marshal(&f)
|
||||
fmt.Println(string(out))
|
||||
|
||||
}
|
||||
|
|
4
http.go
4
http.go
|
@ -57,6 +57,7 @@ func apiV1(broker *Broker, db *db.TelemDb) chi.Router {
|
|||
// this API only accepts JSON.
|
||||
r.Use(middleware.AllowContentType("application/json"))
|
||||
// no caching - always get the latest data.
|
||||
// TODO: add a smart short expiry cache for queries that take a while.
|
||||
r.Use(middleware.NoCache)
|
||||
|
||||
r.Get("/schema", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -78,7 +79,7 @@ func apiV1(broker *Broker, db *db.TelemDb) chi.Router {
|
|||
db.AddEvents(pkgs...)
|
||||
})
|
||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// this should use http query params o return a list of packets.
|
||||
// this should use http query params to return a list of packets.
|
||||
|
||||
})
|
||||
|
||||
|
@ -113,6 +114,7 @@ type apiV1Subscriber struct {
|
|||
idFilter []uint32 // list of Ids to subscribe to. If it's empty, subscribes to all.
|
||||
}
|
||||
|
||||
// this is a websocket stream.
|
||||
func apiV1PacketSubscribe(broker *Broker, db *db.TelemDb) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
conn_id := r.RemoteAddr + uuid.New().String()
|
||||
|
|
|
@ -2,20 +2,27 @@
|
|||
//
|
||||
// It has a generic can Frame (packet), as well as a filter type.
|
||||
// we also define standard interfaces for objects that can accept
|
||||
// can frames. We can use this pattern to easily extend the capabiltiies of the program
|
||||
// by writing "adapters" to various devices/formats (xbee, sqlite, network socket, socketcan)
|
||||
package gotelem
|
||||
// can frames. We can use this pattern to easily extend the capabilities of the program
|
||||
// by writing "adapters" to various devices/formats (xbee, socketcan)
|
||||
package can
|
||||
|
||||
|
||||
type CanID struct {
|
||||
Id uint32
|
||||
Extended bool // since the id itself is not enough.
|
||||
}
|
||||
// Frame represents a protocol-agnostic CAN frame. The Id can be standard or extended,
|
||||
// but if it is extended, the Kind should be EFF.
|
||||
type Frame struct {
|
||||
Id uint32
|
||||
Id CanID
|
||||
Data []byte
|
||||
Kind Kind
|
||||
}
|
||||
|
||||
|
||||
// TODO: should this be replaced
|
||||
type CANFrame interface {
|
||||
Id() uint32
|
||||
Id()
|
||||
Data() []byte
|
||||
Type() Kind
|
||||
}
|
||||
|
@ -26,8 +33,7 @@ type CANFrame interface {
|
|||
type Kind uint8
|
||||
|
||||
const (
|
||||
CanSFFFrame Kind = iota // Standard ID Frame
|
||||
CanEFFFrame // Extended ID Frame
|
||||
CanDataFrame Kind = iota // Standard ID Frame
|
||||
CanRTRFrame // Remote Transmission Request Frame
|
||||
CanErrFrame // Error Frame
|
||||
)
|
|
@ -49,7 +49,7 @@ func OpenTelemDb(path string, options ...TelemDbOption) (tdb *TelemDb, err error
|
|||
return
|
||||
}
|
||||
|
||||
// get latest version of migrations - then run the SQL in order.
|
||||
// get latest version of migrations - then run the SQL in order to perform them
|
||||
fmt.Printf("starting version %d\n", version)
|
||||
|
||||
version, err = RunMigrations(tdb)
|
||||
|
@ -72,7 +72,7 @@ func (tdb *TelemDb) SetVersion(version int) error {
|
|||
|
||||
// sql expression to insert a bus event into the packets database.1
|
||||
const sqlInsertEvent = `
|
||||
INSERT INTO "bus_events" (ts, id, name, data) VALUES ($1, $2, $3, json($4));
|
||||
INSERT INTO "bus_events" (ts, name, data) VALUES ($1, $2, json($3));
|
||||
`
|
||||
|
||||
// AddEvent adds the bus event to the database.
|
||||
|
@ -84,7 +84,12 @@ func (tdb *TelemDb) AddEventsCtx(ctx context.Context, events ...skylab.BusEvent)
|
|||
return
|
||||
}
|
||||
|
||||
for _, b := range events {
|
||||
sqlStmt := sqlInsertEvent
|
||||
const rowSql = "(?, ?, json(?))"
|
||||
inserts := make([]string, len(events))
|
||||
vals := []interface{}{}
|
||||
for idx, b := range events {
|
||||
inserts[idx] = rowSql
|
||||
var j []byte
|
||||
j, err = json.Marshal(b.Data)
|
||||
|
||||
|
@ -92,14 +97,23 @@ func (tdb *TelemDb) AddEventsCtx(ctx context.Context, events ...skylab.BusEvent)
|
|||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, sqlInsertEvent, b.Timestamp.UnixMilli(), b.Id, b.Data.String(), j)
|
||||
vals = append(vals, b.Timestamp.UnixMilli(), b.Data.String(), j)
|
||||
}
|
||||
|
||||
// construct the full statement now
|
||||
sqlStmt = sqlStmt + strings.Join(inserts, ",")
|
||||
stmt, err := tx.PrepareContext(ctx, sqlStmt)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
//TODO: log the number of rows modified/inserted
|
||||
_, err = stmt.ExecContext(ctx, vals...)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
tx.Commit()
|
||||
return
|
||||
}
|
||||
|
@ -109,6 +123,7 @@ func (tdb *TelemDb) AddEvents(events ...skylab.BusEvent) (err error) {
|
|||
return tdb.AddEventsCtx(context.Background(), events...)
|
||||
}
|
||||
|
||||
|
||||
/// Query fragment guide:
|
||||
/// We need to be able to easily construct safe(!) and meaningful queries programatically
|
||||
/// so we make some new types that can be turned into SQL fragments that go inside the where clause.
|
||||
|
@ -121,27 +136,6 @@ type QueryFrag interface {
|
|||
Query() string
|
||||
}
|
||||
|
||||
// QueryIdRange represents a range of IDs to select for, inclusive.
|
||||
type QueryIdRange struct {
|
||||
Start uint32
|
||||
End uint32
|
||||
}
|
||||
|
||||
func (q *QueryIdRange) Query() string {
|
||||
return fmt.Sprintf("id BETWEEN %d AND %d", q.Start, q.End)
|
||||
}
|
||||
|
||||
// QueryIds selects for individual CAN ids
|
||||
type QueryIds []uint32
|
||||
|
||||
func (q QueryIds) Query() string {
|
||||
var idStrings []string
|
||||
for _, id := range q {
|
||||
idStrings = append(idStrings, strconv.FormatUint(uint64(id), 10))
|
||||
}
|
||||
return fmt.Sprintf("id IN (%s)", strings.Join(idStrings, ","))
|
||||
}
|
||||
|
||||
// QueryTimeRange represents a query of a specific time range. For "before" or "after" queries,
|
||||
// use time.Unix(0,0) or time.Now() in start and end respectively.
|
||||
type QueryTimeRange struct {
|
||||
|
@ -204,9 +198,9 @@ func (tdb *TelemDb) GetEvents(limit int, where ...QueryFrag) (events []skylab.Bu
|
|||
|
||||
BusEv := skylab.BusEvent{
|
||||
Timestamp: time.UnixMilli(int64(ev.Timestamp)),
|
||||
Id: ev.Id,
|
||||
Name: ev.Name,
|
||||
}
|
||||
BusEv.Data, err = skylab.FromJson(ev.Id, ev.Data)
|
||||
BusEv.Data, err = skylab.FromJson(ev.Name, ev.Data)
|
||||
|
||||
// FIXME: this is slow!
|
||||
events = append(events, BusEv)
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
CREATE TABLE "bus_events" (
|
||||
"ts" INTEGER NOT NULL, -- timestamp, unix milliseconds
|
||||
"id" INTEGER NOT NULL, -- can ID
|
||||
"name" TEXT NOT NULL, -- name of base packet
|
||||
"data" TEXT NOT NULL CHECK(json_valid(data)) -- JSON object describing the data, including index if any
|
||||
);
|
||||
|
||||
CREATE INDEX "ids_timestamped" ON "bus_events" (
|
||||
"id",
|
||||
"name",
|
||||
"ts" DESC
|
||||
);
|
||||
|
||||
|
|
344
mprpc/rpc.go
344
mprpc/rpc.go
|
@ -1,344 +0,0 @@
|
|||
/*
|
||||
mprpc is a simple bidirectional RPC library using the MessagePack-RPC spec.
|
||||
|
||||
It fully implements the spec and additionally provides Go `error“ handling by
|
||||
converting the error to a standard format for other clients.
|
||||
|
||||
mprpc does not have a typical server/client designation - both use "handlers",
|
||||
which expose methods to be called over the network. A "client" would be an
|
||||
RPCConn which doesn't expose any services, and a "server" would be an RPCConn
|
||||
that doesn't make any `Call`s to the other side.
|
||||
|
||||
This lack of discrete server and client enables mprpc to implement a basic
|
||||
"streaming" architecture on top of the MessagePack-RPC spec, which does not
|
||||
include streaming primitives. Instead, we can provide simple "service handlers"
|
||||
as a callback/destination for streaming data.
|
||||
|
||||
For example, a "client" could subscribe to events from the "server", by
|
||||
providing a callback service to point events to. Then, the "server" would
|
||||
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 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.
|
||||
|
||||
// this is our plain function - we can call it locally to test.
|
||||
func myPlainFunction(p myParam) (r myResult, err error)
|
||||
|
||||
// wrapped is a ServiceFunc that can be passed to rpcConn.RegisterHandler
|
||||
var wrapped := MakeService(myPlainFunction)
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// ServiceFunc is a RPC service handler.
|
||||
// It can be created manually, or by using the generic MakeService function on a
|
||||
//
|
||||
// func(msgp.Encoder) (msgp.Decoder, error)
|
||||
//
|
||||
// type.
|
||||
type ServiceFunc func(params msgp.Raw) (res msgp.Raw, err error)
|
||||
|
||||
// RPCConn is a single RPC communication pair.
|
||||
// It is used by both the
|
||||
// "server" aka listener, and client.
|
||||
type RPCConn struct {
|
||||
// TODO: use io.readwritecloser?
|
||||
rwc io.ReadWriteCloser
|
||||
handlers map[string]ServiceFunc
|
||||
|
||||
ct rpcConnTrack
|
||||
|
||||
logger slog.Logger
|
||||
}
|
||||
|
||||
// creates a new RPC connection on top of an io.ReadWriteCloser. Can be
|
||||
// pre-seeded with handlers.
|
||||
func NewRPC(rwc io.ReadWriteCloser, logger *slog.Logger, initialHandlers map[string]ServiceFunc) (rpc *RPCConn, err error) {
|
||||
|
||||
rpc = &RPCConn{
|
||||
rwc: rwc,
|
||||
handlers: make(map[string]ServiceFunc),
|
||||
ct: NewRPCConnTrack(),
|
||||
}
|
||||
if initialHandlers != nil {
|
||||
for k, v := range initialHandlers {
|
||||
rpc.handlers[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// Call intiates an RPC call to a remote method and returns the
|
||||
// response, or the error, if any. To make calling easier, you can
|
||||
// construct a "Caller" with MakeCaller
|
||||
func (rpc *RPCConn) Call(method string, params msgp.Raw) (msgp.Raw, error) {
|
||||
|
||||
// TODO: error handling.
|
||||
|
||||
id, cb := rpc.ct.Claim()
|
||||
|
||||
req := NewRequest(id, method, params)
|
||||
|
||||
w := msgp.NewWriter(rpc.rwc)
|
||||
req.EncodeMsg(w)
|
||||
|
||||
// block and wait for response.
|
||||
resp := <-cb
|
||||
|
||||
return resp.Result, &resp.Error
|
||||
}
|
||||
|
||||
// Notify initiates a notification to a remote method. It does not
|
||||
// return any information. There is no response from the server.
|
||||
// This method will not block nor will it inform the caller if any errors occur.
|
||||
func (rpc *RPCConn) Notify(method string, params msgp.Raw) {
|
||||
// TODO: return an error if there's a local problem?
|
||||
|
||||
req := NewNotification(method, params)
|
||||
|
||||
w := msgp.NewWriter(rpc.rwc)
|
||||
req.EncodeMsg(w)
|
||||
|
||||
}
|
||||
|
||||
// Register a new handler to be called by the remote side. An error
|
||||
// is returned if the handler name is already in use.
|
||||
func (rpc *RPCConn) RegisterHandler(name string, fn ServiceFunc) error {
|
||||
// TODO: check if name in use.
|
||||
// TODO: mutex lock for sync (or use sync.map?
|
||||
rpc.handlers[name] = fn
|
||||
|
||||
rpc.logger.Info("registered a new handler", "name", name, "fn", fn)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Removes a handler, if it exists. Never errors. No-op if the name
|
||||
// is not a registered handler.
|
||||
func (rpc *RPCConn) RemoveHandler(name string) error {
|
||||
delete(rpc.handlers, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serve runs the server. It will dispatch goroutines to handle each method
|
||||
// call. This can (and should in most cases) be run in the background to allow
|
||||
// for sending and receving on the same connection.
|
||||
func (rpc *RPCConn) Serve() {
|
||||
|
||||
// construct a stream reader.
|
||||
msgReader := msgp.NewReader(rpc.rwc)
|
||||
|
||||
// read a request/notification from the connection.
|
||||
|
||||
var rawmsg msgp.Raw = make(msgp.Raw, 0, 4)
|
||||
|
||||
for {
|
||||
err := rawmsg.DecodeMsg(msgReader)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
rpc.logger.Info("reached EOF, stopping server")
|
||||
return
|
||||
}
|
||||
rpc.logger.Warn("error decoding message", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
rpcIntf, err := parseRPC(rawmsg)
|
||||
|
||||
if err != nil {
|
||||
rpc.logger.Warn("Could not parse RPC message", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch rpcObject := rpcIntf.(type) {
|
||||
case Request:
|
||||
// the object is a request - we must dispatch a goroutine
|
||||
// that will call the handler and also send a return value.
|
||||
go rpc.dispatch(rpcObject)
|
||||
case Notification:
|
||||
go rpc.dispatchNotif(rpcObject)
|
||||
case Response:
|
||||
cbCh, err := rpc.ct.Clear(rpcObject.MsgId)
|
||||
if err != nil {
|
||||
rpc.logger.Warn("could not get rpc callback", "msgid", rpcObject.MsgId, "err", err)
|
||||
continue
|
||||
}
|
||||
cbCh <- rpcObject
|
||||
default:
|
||||
panic("invalid rpcObject!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// INTERNAL functions for rpcConn
|
||||
|
||||
// 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)
|
||||
|
||||
if err != nil {
|
||||
rpc.logger.Warn("error dispatching rpc function", "method", req.Method, "err", err)
|
||||
}
|
||||
// construct the response frame.
|
||||
var rpcE *RPCError = MakeRPCError(err)
|
||||
|
||||
w := msgp.NewWriter(rpc.rwc)
|
||||
|
||||
response := NewResponse(req.MsgId, *rpcE, result)
|
||||
|
||||
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)
|
||||
|
||||
if err != nil {
|
||||
// log the error, but don't do anything about it.
|
||||
rpc.logger.Warn("error dispatching rpc function", "method", req.Method, "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Next, we define some helper generic functions that can be used to make
|
||||
// implementing a msg wrapper easier.
|
||||
|
||||
// msgpackObject is anything that has implemented all the msgpack interfaces.
|
||||
type msgpackObject interface {
|
||||
msgp.Decodable
|
||||
msgp.Encodable
|
||||
msgp.MarshalSizer
|
||||
msgp.Unmarshaler
|
||||
}
|
||||
|
||||
// MakeService is a generic wrapper function. It takes a function with the signature
|
||||
// of func(T msgpObject)(R msgpObject, error) where T and R can be *concrete* types.
|
||||
// and returns a new function that handles conversion to/from msgp.Raw.
|
||||
// The function returned can be used by the RPCConn as a handler function.
|
||||
// This function can typically have it's paramters inferred.
|
||||
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
|
||||
|
||||
_, err := params.UnmarshalMsg(p)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// now, call the function fn with the given params, and record the value.
|
||||
|
||||
resp, err := fn(params)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.MarshalMsg([]byte{})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// should the RPCConn/method name be baked into the function or should they be
|
||||
// part of the returned function paramters?
|
||||
|
||||
// MakeCaller creates a simple wrapper around a parameter of call. The method name
|
||||
// and RPC connection can be given to the returned function to make a RPC call on that
|
||||
// function with the given type parameters.
|
||||
//
|
||||
// This function is slightly obtuse compared to MakeBoundCaller but is more flexible
|
||||
// since you can reuse the same function across multiple connections and method names.
|
||||
//
|
||||
// This generic function must always have it's type paratmers declared explicitly.
|
||||
// They cannot be inferred from the given parameters.
|
||||
func MakeCaller[T, R msgpackObject]() func(string, T, *RPCConn) (R, error) {
|
||||
return func(method string, param T, rpc *RPCConn) (R, error) {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
var resp R
|
||||
|
||||
_, err = resp.UnmarshalMsg(rawResponse)
|
||||
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
// MakeBoundCaller is like MakeCaller, except the RPC connection and method name are
|
||||
// fixed and cannot be adjusted later. This function is more elegant but less flexible
|
||||
// than MakeCaller and should be used when performance is not critical.
|
||||
//
|
||||
// This generic function must always have it's type paratmers declared explicitly.
|
||||
// They cannot be inferred from the given parameters.
|
||||
func MakeBoundCaller[T, R msgpackObject](rpc *RPCConn, method string) func(T) (R, error) {
|
||||
|
||||
return func(param T) (R, error) {
|
||||
// encode parameters
|
||||
// invoke rpc.Call
|
||||
// await response
|
||||
// unpack values.
|
||||
rawParam, _ := param.MarshalMsg([]byte{})
|
||||
|
||||
rawResponse, err := rpc.Call(method, rawParam)
|
||||
if err != nil {
|
||||
var emtpyR R
|
||||
return emtpyR, err
|
||||
}
|
||||
|
||||
var resp R
|
||||
|
||||
_, err = resp.UnmarshalMsg(rawResponse)
|
||||
|
||||
return resp, err
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MakeNotifier creates a new notification function that notifies the remote
|
||||
func MakeNotifier[T msgpackObject](method string) func(T, *RPCConn) error {
|
||||
return func(param T, rpc *RPCConn) error {
|
||||
rawParam, err := param.MarshalMsg([]byte{})
|
||||
rpc.Notify(method, rawParam)
|
||||
return err
|
||||
}
|
||||
}
|
170
mprpc/rpc_msg.go
170
mprpc/rpc_msg.go
|
@ -1,170 +0,0 @@
|
|||
package mprpc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
// this file is a simple implementation of the msgpack-rpc data formats.
|
||||
|
||||
// RPCType is the message type that is being sent.
|
||||
type RPCType int
|
||||
|
||||
const (
|
||||
RequestType RPCType = 0
|
||||
ResponseType RPCType = 1
|
||||
NotificationType RPCType = 2
|
||||
)
|
||||
|
||||
// the messagepack RPC spec requires that the RPC wire formts are ordered arrays,
|
||||
// aka tuples. we can use msgp options to make them tuple automatically,
|
||||
// based on the order they are declared. This makes the order of these
|
||||
// structs *critical*! Do not touch!
|
||||
|
||||
//go:generate msgp
|
||||
//msgp:tuple Request
|
||||
//msgp:tuple Response
|
||||
//msgp:tuple Notification
|
||||
|
||||
// Request represents a function call that expects a Response.
|
||||
type Request struct {
|
||||
// should always be zero.
|
||||
msgtype RPCType `msg:"type"`
|
||||
// MsgId is used to match a Response with a Request
|
||||
MsgId uint32 `msg:"msgid"`
|
||||
// Method is the name of the method/service to execute on the remote
|
||||
Method string `msg:"method"`
|
||||
// Params is the arguments of the method/service. It can be any
|
||||
// MessagePack-serializable type.
|
||||
Params msgp.Raw `msg:"params,allownil"`
|
||||
}
|
||||
|
||||
func NewRequest(msgid uint32, method string, params msgp.Raw) *Request {
|
||||
return &Request{
|
||||
msgtype: 0,
|
||||
MsgId: msgid,
|
||||
Method: method,
|
||||
Params: params,
|
||||
}
|
||||
}
|
||||
|
||||
// A Response is the result and error given from calling a service.
|
||||
type Response struct {
|
||||
// should always be one.
|
||||
msgtype RPCType `msg:"type"`
|
||||
// MsgId is an identifier used to match this Response with the Request that created it.
|
||||
MsgId uint32 `msg:"msgid"`
|
||||
// Error is the error encountered while attempting to execute the method, if any.
|
||||
Error RPCError `msg:"error,allownil"`
|
||||
// Result is the raw object that was returned by the calling method. It
|
||||
// can be any MessagePack-serializable object.
|
||||
Result msgp.Raw `msg:"result,allownil"`
|
||||
}
|
||||
|
||||
func NewResponse(msgid uint32, respErr RPCError, res msgp.Raw) *Response {
|
||||
return &Response{
|
||||
msgtype: 1,
|
||||
MsgId: msgid,
|
||||
Error: respErr,
|
||||
Result: res,
|
||||
}
|
||||
}
|
||||
|
||||
// A notification is a function call that does not care if the call
|
||||
// succeeds and ignores responses.
|
||||
type Notification struct {
|
||||
// should always be *2*
|
||||
msgtype RPCType `msg:"type"`
|
||||
Method string `msg:"method"`
|
||||
Params msgp.Raw `msg:"params,allownil"`
|
||||
}
|
||||
|
||||
func NewNotification(method string, params msgp.Raw) *Notification {
|
||||
return &Notification{
|
||||
msgtype: 2,
|
||||
Method: method,
|
||||
Params: params,
|
||||
}
|
||||
}
|
||||
|
||||
// getMsgType uses raw messagpack RPC to return the underlying message type from
|
||||
// the raw array given by b.
|
||||
func getMsgType(b msgp.Raw) RPCType {
|
||||
size, next, err := msgp.ReadArrayHeaderBytes(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if size == 3 { // hot path for notifications.
|
||||
return NotificationType
|
||||
}
|
||||
|
||||
vtype, _, err := msgp.ReadIntBytes(next)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// todo: use readIntf instead? returns a []interface{} and we can map it ourselves...
|
||||
return RPCType(vtype)
|
||||
}
|
||||
|
||||
// parseRPC takes a raw message and decodes it based on the first value
|
||||
// of the array (the type). It returns the decoded object. Callers
|
||||
// can use a type-switch to determine the type of the data.
|
||||
func parseRPC(raw msgp.Raw) (interface{}, error) {
|
||||
t := getMsgType(raw)
|
||||
|
||||
switch RPCType(t) {
|
||||
|
||||
case RequestType:
|
||||
// create and return a request struct.
|
||||
req := &Request{}
|
||||
_, err := req.UnmarshalMsg(raw)
|
||||
return req, err
|
||||
case ResponseType:
|
||||
res := &Response{}
|
||||
_, err := res.UnmarshalMsg(raw)
|
||||
return res, err
|
||||
case NotificationType:
|
||||
notif := &Notification{}
|
||||
_, err := notif.UnmarshalMsg(raw)
|
||||
return notif, err
|
||||
default:
|
||||
// uh oh.
|
||||
return nil, errors.New("unmatched RPC type")
|
||||
}
|
||||
}
|
||||
|
||||
//msgp:tuple RPCError
|
||||
|
||||
// RPCError is a common RPC error format. It is basically a clone of the
|
||||
// JSON-RPC error format. We use it so we know what to expect there.
|
||||
type RPCError struct {
|
||||
Code int
|
||||
Desc string
|
||||
}
|
||||
|
||||
// Converts a Go error into a RPC error.
|
||||
func MakeRPCError(err error) *RPCError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &RPCError{
|
||||
Code: -1,
|
||||
Desc: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the Error interface for RPCError
|
||||
func (r *RPCError) Error() string {
|
||||
return r.Desc
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// we need to describe an empty data that will be excluded in the msgp
|
||||
// for functions without an argument or return value.
|
||||
type RPCEmpty struct {
|
||||
}
|
|
@ -1,577 +0,0 @@
|
|||
package mprpc
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *Notification) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if zb0001 != 2 {
|
||||
err = msgp.ArrayError{Wanted: 2, Got: zb0001}
|
||||
return
|
||||
}
|
||||
z.Method, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Method")
|
||||
return
|
||||
}
|
||||
err = z.Params.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Params")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *Notification) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// array header, size 2
|
||||
err = en.Append(0x92)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Method)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Method")
|
||||
return
|
||||
}
|
||||
err = z.Params.EncodeMsg(en)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Params")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *Notification) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// array header, size 2
|
||||
o = append(o, 0x92)
|
||||
o = msgp.AppendString(o, z.Method)
|
||||
o, err = z.Params.MarshalMsg(o)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Params")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *Notification) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if zb0001 != 2 {
|
||||
err = msgp.ArrayError{Wanted: 2, Got: zb0001}
|
||||
return
|
||||
}
|
||||
z.Method, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Method")
|
||||
return
|
||||
}
|
||||
bts, err = z.Params.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Params")
|
||||
return
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *Notification) Msgsize() (s int) {
|
||||
s = 1 + msgp.StringPrefixSize + len(z.Method) + z.Params.Msgsize()
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *RPCEmpty) 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) {
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z RPCEmpty) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 0
|
||||
err = en.Append(0x80)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z RPCEmpty) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 0
|
||||
o = append(o, 0x80)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *RPCEmpty) 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) {
|
||||
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 RPCEmpty) Msgsize() (s int) {
|
||||
s = 1
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *RPCError) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if zb0001 != 2 {
|
||||
err = msgp.ArrayError{Wanted: 2, Got: zb0001}
|
||||
return
|
||||
}
|
||||
z.Code, err = dc.ReadInt()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Code")
|
||||
return
|
||||
}
|
||||
z.Desc, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Desc")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z RPCError) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// array header, size 2
|
||||
err = en.Append(0x92)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt(z.Code)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Code")
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Desc)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Desc")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z RPCError) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// array header, size 2
|
||||
o = append(o, 0x92)
|
||||
o = msgp.AppendInt(o, z.Code)
|
||||
o = msgp.AppendString(o, z.Desc)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *RPCError) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if zb0001 != 2 {
|
||||
err = msgp.ArrayError{Wanted: 2, Got: zb0001}
|
||||
return
|
||||
}
|
||||
z.Code, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Code")
|
||||
return
|
||||
}
|
||||
z.Desc, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Desc")
|
||||
return
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z RPCError) Msgsize() (s int) {
|
||||
s = 1 + msgp.IntSize + msgp.StringPrefixSize + len(z.Desc)
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *RPCType) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
{
|
||||
var zb0001 int
|
||||
zb0001, err = dc.ReadInt()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
(*z) = RPCType(zb0001)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z RPCType) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = en.WriteInt(int(z))
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z RPCType) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
o = msgp.AppendInt(o, int(z))
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *RPCType) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
{
|
||||
var zb0001 int
|
||||
zb0001, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
(*z) = RPCType(zb0001)
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z RPCType) Msgsize() (s int) {
|
||||
s = msgp.IntSize
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *Request) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if zb0001 != 3 {
|
||||
err = msgp.ArrayError{Wanted: 3, Got: zb0001}
|
||||
return
|
||||
}
|
||||
z.MsgId, err = dc.ReadUint32()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MsgId")
|
||||
return
|
||||
}
|
||||
z.Method, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Method")
|
||||
return
|
||||
}
|
||||
err = z.Params.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Params")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *Request) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// array header, size 3
|
||||
err = en.Append(0x93)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteUint32(z.MsgId)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MsgId")
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Method)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Method")
|
||||
return
|
||||
}
|
||||
err = z.Params.EncodeMsg(en)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Params")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *Request) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// array header, size 3
|
||||
o = append(o, 0x93)
|
||||
o = msgp.AppendUint32(o, z.MsgId)
|
||||
o = msgp.AppendString(o, z.Method)
|
||||
o, err = z.Params.MarshalMsg(o)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Params")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *Request) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if zb0001 != 3 {
|
||||
err = msgp.ArrayError{Wanted: 3, Got: zb0001}
|
||||
return
|
||||
}
|
||||
z.MsgId, bts, err = msgp.ReadUint32Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MsgId")
|
||||
return
|
||||
}
|
||||
z.Method, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Method")
|
||||
return
|
||||
}
|
||||
bts, err = z.Params.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Params")
|
||||
return
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *Request) Msgsize() (s int) {
|
||||
s = 1 + msgp.Uint32Size + msgp.StringPrefixSize + len(z.Method) + z.Params.Msgsize()
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *Response) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if zb0001 != 3 {
|
||||
err = msgp.ArrayError{Wanted: 3, Got: zb0001}
|
||||
return
|
||||
}
|
||||
z.MsgId, err = dc.ReadUint32()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MsgId")
|
||||
return
|
||||
}
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Error")
|
||||
return
|
||||
}
|
||||
if zb0002 != 2 {
|
||||
err = msgp.ArrayError{Wanted: 2, Got: zb0002}
|
||||
return
|
||||
}
|
||||
z.Error.Code, err = dc.ReadInt()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Error", "Code")
|
||||
return
|
||||
}
|
||||
z.Error.Desc, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Error", "Desc")
|
||||
return
|
||||
}
|
||||
err = z.Result.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Result")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *Response) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// array header, size 3
|
||||
err = en.Append(0x93)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteUint32(z.MsgId)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MsgId")
|
||||
return
|
||||
}
|
||||
// array header, size 2
|
||||
err = en.Append(0x92)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt(z.Error.Code)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Error", "Code")
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Error.Desc)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Error", "Desc")
|
||||
return
|
||||
}
|
||||
err = z.Result.EncodeMsg(en)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Result")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *Response) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// array header, size 3
|
||||
o = append(o, 0x93)
|
||||
o = msgp.AppendUint32(o, z.MsgId)
|
||||
// array header, size 2
|
||||
o = append(o, 0x92)
|
||||
o = msgp.AppendInt(o, z.Error.Code)
|
||||
o = msgp.AppendString(o, z.Error.Desc)
|
||||
o, err = z.Result.MarshalMsg(o)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Result")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *Response) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
if zb0001 != 3 {
|
||||
err = msgp.ArrayError{Wanted: 3, Got: zb0001}
|
||||
return
|
||||
}
|
||||
z.MsgId, bts, err = msgp.ReadUint32Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MsgId")
|
||||
return
|
||||
}
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Error")
|
||||
return
|
||||
}
|
||||
if zb0002 != 2 {
|
||||
err = msgp.ArrayError{Wanted: 2, Got: zb0002}
|
||||
return
|
||||
}
|
||||
z.Error.Code, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Error", "Code")
|
||||
return
|
||||
}
|
||||
z.Error.Desc, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Error", "Desc")
|
||||
return
|
||||
}
|
||||
bts, err = z.Result.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Result")
|
||||
return
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *Response) Msgsize() (s int) {
|
||||
s = 1 + msgp.Uint32Size + 1 + msgp.IntSize + msgp.StringPrefixSize + len(z.Error.Desc) + z.Result.Msgsize()
|
||||
return
|
||||
}
|
|
@ -1,575 +0,0 @@
|
|||
package mprpc
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
func TestMarshalUnmarshalNotification(t *testing.T) {
|
||||
v := Notification{}
|
||||
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 BenchmarkMarshalMsgNotification(b *testing.B) {
|
||||
v := Notification{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgNotification(b *testing.B) {
|
||||
v := Notification{}
|
||||
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 BenchmarkUnmarshalNotification(b *testing.B) {
|
||||
v := Notification{}
|
||||
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 TestEncodeDecodeNotification(t *testing.T) {
|
||||
v := Notification{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodeNotification Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := Notification{}
|
||||
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 BenchmarkEncodeNotification(b *testing.B) {
|
||||
v := Notification{}
|
||||
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 BenchmarkDecodeNotification(b *testing.B) {
|
||||
v := Notification{}
|
||||
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 TestMarshalUnmarshalRPCEmpty(t *testing.T) {
|
||||
v := RPCEmpty{}
|
||||
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 BenchmarkMarshalMsgRPCEmpty(b *testing.B) {
|
||||
v := RPCEmpty{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgRPCEmpty(b *testing.B) {
|
||||
v := RPCEmpty{}
|
||||
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 BenchmarkUnmarshalRPCEmpty(b *testing.B) {
|
||||
v := RPCEmpty{}
|
||||
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 TestEncodeDecodeRPCEmpty(t *testing.T) {
|
||||
v := RPCEmpty{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodeRPCEmpty Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := RPCEmpty{}
|
||||
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 BenchmarkEncodeRPCEmpty(b *testing.B) {
|
||||
v := RPCEmpty{}
|
||||
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 BenchmarkDecodeRPCEmpty(b *testing.B) {
|
||||
v := RPCEmpty{}
|
||||
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 TestMarshalUnmarshalRPCError(t *testing.T) {
|
||||
v := RPCError{}
|
||||
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 BenchmarkMarshalMsgRPCError(b *testing.B) {
|
||||
v := RPCError{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgRPCError(b *testing.B) {
|
||||
v := RPCError{}
|
||||
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 BenchmarkUnmarshalRPCError(b *testing.B) {
|
||||
v := RPCError{}
|
||||
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 TestEncodeDecodeRPCError(t *testing.T) {
|
||||
v := RPCError{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodeRPCError Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := RPCError{}
|
||||
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 BenchmarkEncodeRPCError(b *testing.B) {
|
||||
v := RPCError{}
|
||||
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 BenchmarkDecodeRPCError(b *testing.B) {
|
||||
v := RPCError{}
|
||||
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 TestMarshalUnmarshalRequest(t *testing.T) {
|
||||
v := Request{}
|
||||
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 BenchmarkMarshalMsgRequest(b *testing.B) {
|
||||
v := Request{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgRequest(b *testing.B) {
|
||||
v := Request{}
|
||||
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 BenchmarkUnmarshalRequest(b *testing.B) {
|
||||
v := Request{}
|
||||
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 TestEncodeDecodeRequest(t *testing.T) {
|
||||
v := Request{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodeRequest Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := Request{}
|
||||
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 BenchmarkEncodeRequest(b *testing.B) {
|
||||
v := Request{}
|
||||
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 BenchmarkDecodeRequest(b *testing.B) {
|
||||
v := Request{}
|
||||
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 TestMarshalUnmarshalResponse(t *testing.T) {
|
||||
v := Response{}
|
||||
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 BenchmarkMarshalMsgResponse(b *testing.B) {
|
||||
v := Response{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgResponse(b *testing.B) {
|
||||
v := Response{}
|
||||
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 BenchmarkUnmarshalResponse(b *testing.B) {
|
||||
v := Response{}
|
||||
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 TestEncodeDecodeResponse(t *testing.T) {
|
||||
v := Response{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodeResponse Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := Response{}
|
||||
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 BenchmarkEncodeResponse(b *testing.B) {
|
||||
v := Response{}
|
||||
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 BenchmarkDecodeResponse(b *testing.B) {
|
||||
v := Response{}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
package mprpc_test
|
|
@ -1,69 +0,0 @@
|
|||
package mprpc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// RPCConntrack is a request-response tracker that is used to connect
|
||||
// the response to the appropriate caller.
|
||||
type rpcConnTrack struct {
|
||||
ct map[uint32]chan Response
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
|
||||
func NewRPCConnTrack() rpcConnTrack {
|
||||
return rpcConnTrack{
|
||||
ct: make(map[uint32]chan Response),
|
||||
}
|
||||
}
|
||||
|
||||
// Get attempts to get a random mark from the mutex.
|
||||
func (c *rpcConnTrack) Claim() (uint32, chan Response) {
|
||||
var val uint32
|
||||
for {
|
||||
|
||||
//
|
||||
newVal := rand.Uint32()
|
||||
|
||||
// BUG(saji): rpcConnTrack collisions are inefficient.
|
||||
|
||||
// collision is *rare* - so we just try again.
|
||||
// I hope to god you don't saturate this tracker.
|
||||
c.mu.RLock()
|
||||
if _, exist := c.ct[newVal]; !exist {
|
||||
val = newVal
|
||||
c.mu.RUnlock()
|
||||
break
|
||||
}
|
||||
c.mu.RUnlock()
|
||||
}
|
||||
|
||||
// claim it
|
||||
// the channel should be buffered. We only expect one value to go through.
|
||||
// so the size is fixed to 1.
|
||||
ch := make(chan Response, 1)
|
||||
c.mu.Lock()
|
||||
c.ct[val] = ch
|
||||
c.mu.Unlock()
|
||||
|
||||
return val, ch
|
||||
}
|
||||
|
||||
// Clear deletes the connection from the tracker and returns the channel
|
||||
// associated with it. The caller can use the channel afterwards
|
||||
// to send the response. It is the caller's responsibility to close the channel.
|
||||
func (c *rpcConnTrack) Clear(val uint32) (chan Response, error) {
|
||||
c.mu.RLock()
|
||||
ch, ok := c.ct[val]
|
||||
c.mu.RUnlock()
|
||||
if !ok {
|
||||
return nil, errors.New("invalid msg id")
|
||||
}
|
||||
c.mu.Lock()
|
||||
delete(c.ct, val)
|
||||
c.mu.Unlock()
|
||||
return ch, nil
|
||||
}
|
|
@ -18,36 +18,37 @@ import (
|
|||
|
||||
// SkylabFile is a yaml file from skylab.
|
||||
type SkylabFile struct {
|
||||
Packets []PacketDef `json:"packets"`
|
||||
Boards []BoardDef `json:"boards"`
|
||||
Packets []PacketDef `yaml:"packets"`
|
||||
Boards []BoardDef `yaml:"boards"`
|
||||
}
|
||||
|
||||
type BoardDef struct {
|
||||
Name string `json:"name"`
|
||||
Transmit []string `json:"transmit"`
|
||||
Receive []string `json:"receive"`
|
||||
Name string `yaml:"name"`
|
||||
Transmit []string `yaml:"transmit"`
|
||||
Receive []string `yaml:"receive"`
|
||||
}
|
||||
|
||||
// data field.
|
||||
type FieldDef struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Units string `json:"units"`
|
||||
Conversion float32 `json:"conversion"`
|
||||
Name string `yaml:"name"`
|
||||
Type string `yaml:"type"`
|
||||
Units string `yaml:"units"`
|
||||
Conversion float32 `yaml:"conversion"`
|
||||
Bits []struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"bits"`
|
||||
Name string `yaml:"name"`
|
||||
} `yaml:"bits"`
|
||||
}
|
||||
|
||||
// a PacketDef is a full can packet.
|
||||
type PacketDef struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Id uint32 `json:"id"`
|
||||
Endian string `json:"endian"`
|
||||
Repeat int `json:"repeat"`
|
||||
Offset int `json:"offset"`
|
||||
Data []FieldDef `json:"data"`
|
||||
Name string `yaml:"name"`
|
||||
Description string `yaml:"description"`
|
||||
Id uint32 `yaml:"id"`
|
||||
Endian string `yaml:"endian"`
|
||||
Extended bool `yaml:"is_extended"`
|
||||
Repeat int `yaml:"repeat"`
|
||||
Offset int `yaml:"offset"`
|
||||
Data []FieldDef `yaml:"data"`
|
||||
}
|
||||
|
||||
// we need to generate bitfield types.
|
||||
|
@ -273,6 +274,20 @@ func mapf(format string, els []int) []string {
|
|||
return resp
|
||||
}
|
||||
|
||||
func idToString(p PacketDef) string {
|
||||
if p.Repeat > 0 {
|
||||
resp := make([]string, p.Repeat)
|
||||
for idx := 0; idx < p.Repeat; idx++ {
|
||||
resp[idx] = fmt.Sprintf("can.CanID{ Id: 0x%X, Extended: %t }", int(p.Id)+idx*p.Offset, p.Extended)
|
||||
}
|
||||
|
||||
return strings.Join(resp, ",")
|
||||
|
||||
} else {
|
||||
return fmt.Sprintf("can.CanID{ Id: 0x%X, Extended: %t }", p.Id, p.Extended)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// read path as the first arg, glob it for yamls, read each yaml into a skylabFile.
|
||||
// then take each skylab file, put all the packets into one big array.
|
||||
|
@ -318,6 +333,7 @@ func main() {
|
|||
"mapf": mapf,
|
||||
"maptype": MapType,
|
||||
"json": json.Marshal,
|
||||
"idToString": idToString,
|
||||
}
|
||||
|
||||
tmpl, err := template.New("golang.go.tmpl").Funcs(fnMap).ParseGlob("templates/*.go.tmpl")
|
||||
|
|
|
@ -84,18 +84,16 @@ func ToCanFrame(p Packet) (id uint32, data []byte, err error) {
|
|||
// ---- other wire encoding business ----
|
||||
|
||||
// internal structure for partially decoding json object.
|
||||
// includes
|
||||
type RawJsonEvent struct {
|
||||
Timestamp int64 `json:"ts" db:"ts"`
|
||||
Id uint32 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
// BusEvent is a timestamped Skylab packet
|
||||
// BusEvent is a timestamped Skylab packet - it contains
|
||||
type BusEvent struct {
|
||||
Timestamp time.Time `json:"ts"`
|
||||
Id uint32 `json:"id"`
|
||||
Name string `json:"id"`
|
||||
Data Packet `json:"data"`
|
||||
}
|
||||
|
||||
|
@ -103,10 +101,10 @@ func (e BusEvent) MarshalJSON() (b []byte, err error) {
|
|||
// create the underlying raw event
|
||||
j := &RawJsonEvent{
|
||||
Timestamp: e.Timestamp.UnixMilli(),
|
||||
Id: e.Id,
|
||||
Name: e.Data.String(),
|
||||
Name: e.Name,
|
||||
}
|
||||
// now we use the magic Packet -> map[string]interface{} function
|
||||
// FIXME: this uses reflection and isn't good for the economy
|
||||
j.Data, err = json.Marshal(e.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -126,42 +124,12 @@ func (e *BusEvent) UnmarshalJSON(b []byte) error {
|
|||
}
|
||||
|
||||
e.Timestamp = time.UnixMilli(j.Timestamp)
|
||||
e.Id = j.Id
|
||||
e.Data, err = FromJson(j.Id, j.Data)
|
||||
e.Name = j.Name
|
||||
e.Data, err = FromJson(j.Name, j.Data)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *BusEvent) MarshalMsg(b []byte) ([]byte, error) {
|
||||
|
||||
// we need to send the bytes as a []byte instead of
|
||||
// an object like the JSON one (lose self-documenting)
|
||||
data, err := e.Data.MarshalPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawEv := &msgpRawEvent{
|
||||
Timestamp: uint32(e.Timestamp.UnixMilli()),
|
||||
Id: uint32(e.Id),
|
||||
Data: data,
|
||||
}
|
||||
|
||||
return rawEv.MarshalMsg(b)
|
||||
}
|
||||
|
||||
func (e *BusEvent) UnmarshalMsg(b []byte) ([]byte, error) {
|
||||
rawEv := &msgpRawEvent{}
|
||||
remain, err := rawEv.UnmarshalMsg(b)
|
||||
if err != nil {
|
||||
return remain, err
|
||||
}
|
||||
e.Timestamp = time.UnixMilli(int64(rawEv.Timestamp))
|
||||
e.Id = rawEv.Id
|
||||
e.Data, err = FromCanFrame(rawEv.Id, rawEv.Data)
|
||||
|
||||
return remain, err
|
||||
}
|
||||
|
||||
// we need to be able to parse the JSON as well. this is done using the
|
||||
// generator since we can use the switch/case thing since it's the fastest
|
||||
|
||||
|
|
1436
skylab/skylab_gen.go
1436
skylab/skylab_gen.go
File diff suppressed because one or more lines are too long
|
@ -402,6 +402,78 @@ func TestJSONBmsChargerResponse(t *testing.T) {
|
|||
}
|
||||
|
||||
|
||||
}
|
||||
func TestMarshalUnmarshalChassisIsolationFault(t *testing.T) {
|
||||
v := &ChassisIsolationFault{}
|
||||
bin, err := v.MarshalPacket()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = v.UnmarshalPacket(bin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONChassisIsolationFault(t *testing.T) {
|
||||
|
||||
v := &ChassisIsolationFault{}
|
||||
|
||||
rawData, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
id, _ := v.CANId()
|
||||
p, err := FromJson(id, rawData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
switch underlying := p.(type) {
|
||||
case *ChassisIsolationFault:
|
||||
break
|
||||
default:
|
||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
func TestMarshalUnmarshalBmsImdInfo(t *testing.T) {
|
||||
v := &BmsImdInfo{}
|
||||
bin, err := v.MarshalPacket()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = v.UnmarshalPacket(bin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONBmsImdInfo(t *testing.T) {
|
||||
|
||||
v := &BmsImdInfo{}
|
||||
|
||||
rawData, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
id, _ := v.CANId()
|
||||
p, err := FromJson(id, rawData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
switch underlying := p.(type) {
|
||||
case *BmsImdInfo:
|
||||
break
|
||||
default:
|
||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
func TestMarshalUnmarshalDashboardPedalPercentages(t *testing.T) {
|
||||
v := &DashboardPedalPercentages{}
|
||||
|
@ -762,6 +834,78 @@ func TestJSONArrayPower(t *testing.T) {
|
|||
}
|
||||
|
||||
|
||||
}
|
||||
func TestMarshalUnmarshalArrayEnergy(t *testing.T) {
|
||||
v := &ArrayEnergy{}
|
||||
bin, err := v.MarshalPacket()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = v.UnmarshalPacket(bin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONArrayEnergy(t *testing.T) {
|
||||
|
||||
v := &ArrayEnergy{}
|
||||
|
||||
rawData, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
id, _ := v.CANId()
|
||||
p, err := FromJson(id, rawData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
switch underlying := p.(type) {
|
||||
case *ArrayEnergy:
|
||||
break
|
||||
default:
|
||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
func TestMarshalUnmarshalArrayEnergyReset(t *testing.T) {
|
||||
v := &ArrayEnergyReset{}
|
||||
bin, err := v.MarshalPacket()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = v.UnmarshalPacket(bin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONArrayEnergyReset(t *testing.T) {
|
||||
|
||||
v := &ArrayEnergyReset{}
|
||||
|
||||
rawData, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
id, _ := v.CANId()
|
||||
p, err := FromJson(id, rawData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
switch underlying := p.(type) {
|
||||
case *ArrayEnergyReset:
|
||||
break
|
||||
default:
|
||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
func TestMarshalUnmarshalVisionTurnSignalsCommand(t *testing.T) {
|
||||
v := &VisionTurnSignalsCommand{}
|
||||
|
@ -1663,8 +1807,8 @@ func TestJSONTrackerData(t *testing.T) {
|
|||
|
||||
|
||||
}
|
||||
func TestMarshalUnmarshalTritiumMotorDrive(t *testing.T) {
|
||||
v := &TritiumMotorDrive{}
|
||||
func TestMarshalUnmarshalTritiumMotorDriveL(t *testing.T) {
|
||||
v := &TritiumMotorDriveL{}
|
||||
bin, err := v.MarshalPacket()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -1675,9 +1819,9 @@ func TestMarshalUnmarshalTritiumMotorDrive(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestJSONTritiumMotorDrive(t *testing.T) {
|
||||
func TestJSONTritiumMotorDriveL(t *testing.T) {
|
||||
|
||||
v := &TritiumMotorDrive{}
|
||||
v := &TritiumMotorDriveL{}
|
||||
|
||||
rawData, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
|
@ -1691,7 +1835,7 @@ func TestJSONTritiumMotorDrive(t *testing.T) {
|
|||
}
|
||||
|
||||
switch underlying := p.(type) {
|
||||
case *TritiumMotorDrive:
|
||||
case *TritiumMotorDriveL:
|
||||
break
|
||||
default:
|
||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
||||
|
@ -1699,8 +1843,8 @@ func TestJSONTritiumMotorDrive(t *testing.T) {
|
|||
|
||||
|
||||
}
|
||||
func TestMarshalUnmarshalTritiumMotorPower(t *testing.T) {
|
||||
v := &TritiumMotorPower{}
|
||||
func TestMarshalUnmarshalTritiumMotorPowerL(t *testing.T) {
|
||||
v := &TritiumMotorPowerL{}
|
||||
bin, err := v.MarshalPacket()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -1711,9 +1855,9 @@ func TestMarshalUnmarshalTritiumMotorPower(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestJSONTritiumMotorPower(t *testing.T) {
|
||||
func TestJSONTritiumMotorPowerL(t *testing.T) {
|
||||
|
||||
v := &TritiumMotorPower{}
|
||||
v := &TritiumMotorPowerL{}
|
||||
|
||||
rawData, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
|
@ -1727,7 +1871,7 @@ func TestJSONTritiumMotorPower(t *testing.T) {
|
|||
}
|
||||
|
||||
switch underlying := p.(type) {
|
||||
case *TritiumMotorPower:
|
||||
case *TritiumMotorPowerL:
|
||||
break
|
||||
default:
|
||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
||||
|
@ -1735,8 +1879,8 @@ func TestJSONTritiumMotorPower(t *testing.T) {
|
|||
|
||||
|
||||
}
|
||||
func TestMarshalUnmarshalTritiumReset(t *testing.T) {
|
||||
v := &TritiumReset{}
|
||||
func TestMarshalUnmarshalTritiumResetL(t *testing.T) {
|
||||
v := &TritiumResetL{}
|
||||
bin, err := v.MarshalPacket()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -1747,9 +1891,9 @@ func TestMarshalUnmarshalTritiumReset(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestJSONTritiumReset(t *testing.T) {
|
||||
func TestJSONTritiumResetL(t *testing.T) {
|
||||
|
||||
v := &TritiumReset{}
|
||||
v := &TritiumResetL{}
|
||||
|
||||
rawData, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
|
@ -1763,7 +1907,115 @@ func TestJSONTritiumReset(t *testing.T) {
|
|||
}
|
||||
|
||||
switch underlying := p.(type) {
|
||||
case *TritiumReset:
|
||||
case *TritiumResetL:
|
||||
break
|
||||
default:
|
||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
func TestMarshalUnmarshalTritiumMotorDriveR(t *testing.T) {
|
||||
v := &TritiumMotorDriveR{}
|
||||
bin, err := v.MarshalPacket()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = v.UnmarshalPacket(bin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONTritiumMotorDriveR(t *testing.T) {
|
||||
|
||||
v := &TritiumMotorDriveR{}
|
||||
|
||||
rawData, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
id, _ := v.CANId()
|
||||
p, err := FromJson(id, rawData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
switch underlying := p.(type) {
|
||||
case *TritiumMotorDriveR:
|
||||
break
|
||||
default:
|
||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
func TestMarshalUnmarshalTritiumMotorPowerR(t *testing.T) {
|
||||
v := &TritiumMotorPowerR{}
|
||||
bin, err := v.MarshalPacket()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = v.UnmarshalPacket(bin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONTritiumMotorPowerR(t *testing.T) {
|
||||
|
||||
v := &TritiumMotorPowerR{}
|
||||
|
||||
rawData, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
id, _ := v.CANId()
|
||||
p, err := FromJson(id, rawData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
switch underlying := p.(type) {
|
||||
case *TritiumMotorPowerR:
|
||||
break
|
||||
default:
|
||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
func TestMarshalUnmarshalTritiumResetR(t *testing.T) {
|
||||
v := &TritiumResetR{}
|
||||
bin, err := v.MarshalPacket()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = v.UnmarshalPacket(bin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONTritiumResetR(t *testing.T) {
|
||||
|
||||
v := &TritiumResetR{}
|
||||
|
||||
rawData, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
id, _ := v.CANId()
|
||||
p, err := FromJson(id, rawData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
switch underlying := p.(type) {
|
||||
case *TritiumResetR:
|
||||
break
|
||||
default:
|
||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
package skylab
|
||||
|
||||
//go:generate msgp -unexported
|
||||
|
||||
// internal structure for handling
|
||||
type msgpRawEvent struct {
|
||||
Timestamp uint32 `msg:"ts"`
|
||||
Id uint32 `msg:"id"`
|
||||
Data []byte `msg:"data"`
|
||||
}
|
||||
|
||||
// internal structure to represent a raw can packet over the network.
|
||||
// this is what's sent over the solar car to lead xbee connection
|
||||
// for brevity while still having some robustness.
|
||||
type msgpRawPacket struct {
|
||||
Id uint32 `msg:"id"`
|
||||
Data []byte `msg:"data"`
|
||||
}
|
|
@ -1,288 +0,0 @@
|
|||
package skylab
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *msgpRawEvent) 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 "ts":
|
||||
z.Timestamp, err = dc.ReadUint32()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Timestamp")
|
||||
return
|
||||
}
|
||||
case "id":
|
||||
z.Id, err = dc.ReadUint32()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Id")
|
||||
return
|
||||
}
|
||||
case "data":
|
||||
z.Data, err = dc.ReadBytes(z.Data)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Data")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *msgpRawEvent) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 3
|
||||
// write "ts"
|
||||
err = en.Append(0x83, 0xa2, 0x74, 0x73)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteUint32(z.Timestamp)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Timestamp")
|
||||
return
|
||||
}
|
||||
// write "id"
|
||||
err = en.Append(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.Data)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Data")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *msgpRawEvent) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 3
|
||||
// string "ts"
|
||||
o = append(o, 0x83, 0xa2, 0x74, 0x73)
|
||||
o = msgp.AppendUint32(o, z.Timestamp)
|
||||
// string "id"
|
||||
o = append(o, 0xa2, 0x69, 0x64)
|
||||
o = msgp.AppendUint32(o, z.Id)
|
||||
// string "data"
|
||||
o = append(o, 0xa4, 0x64, 0x61, 0x74, 0x61)
|
||||
o = msgp.AppendBytes(o, z.Data)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *msgpRawEvent) 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 "ts":
|
||||
z.Timestamp, bts, err = msgp.ReadUint32Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Timestamp")
|
||||
return
|
||||
}
|
||||
case "id":
|
||||
z.Id, bts, err = msgp.ReadUint32Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Id")
|
||||
return
|
||||
}
|
||||
case "data":
|
||||
z.Data, bts, err = msgp.ReadBytesBytes(bts, z.Data)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Data")
|
||||
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 *msgpRawEvent) Msgsize() (s int) {
|
||||
s = 1 + 3 + msgp.Uint32Size + 3 + msgp.Uint32Size + 5 + msgp.BytesPrefixSize + len(z.Data)
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *msgpRawPacket) 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.Data, err = dc.ReadBytes(z.Data)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Data")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *msgpRawPacket) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 2
|
||||
// write "id"
|
||||
err = en.Append(0x82, 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.Data)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Data")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *msgpRawPacket) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 2
|
||||
// string "id"
|
||||
o = append(o, 0x82, 0xa2, 0x69, 0x64)
|
||||
o = msgp.AppendUint32(o, z.Id)
|
||||
// string "data"
|
||||
o = append(o, 0xa4, 0x64, 0x61, 0x74, 0x61)
|
||||
o = msgp.AppendBytes(o, z.Data)
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *msgpRawPacket) 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.Data, bts, err = msgp.ReadBytesBytes(bts, z.Data)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Data")
|
||||
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 *msgpRawPacket) Msgsize() (s int) {
|
||||
s = 1 + 3 + msgp.Uint32Size + 5 + msgp.BytesPrefixSize + len(z.Data)
|
||||
return
|
||||
}
|
|
@ -1,236 +0,0 @@
|
|||
package skylab
|
||||
|
||||
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
func TestMarshalUnmarshalmsgpRawEvent(t *testing.T) {
|
||||
v := msgpRawEvent{}
|
||||
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 BenchmarkMarshalMsgmsgpRawEvent(b *testing.B) {
|
||||
v := msgpRawEvent{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgmsgpRawEvent(b *testing.B) {
|
||||
v := msgpRawEvent{}
|
||||
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 BenchmarkUnmarshalmsgpRawEvent(b *testing.B) {
|
||||
v := msgpRawEvent{}
|
||||
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 TestEncodeDecodemsgpRawEvent(t *testing.T) {
|
||||
v := msgpRawEvent{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodemsgpRawEvent Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := msgpRawEvent{}
|
||||
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 BenchmarkEncodemsgpRawEvent(b *testing.B) {
|
||||
v := msgpRawEvent{}
|
||||
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 BenchmarkDecodemsgpRawEvent(b *testing.B) {
|
||||
v := msgpRawEvent{}
|
||||
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 TestMarshalUnmarshalmsgpRawPacket(t *testing.T) {
|
||||
v := msgpRawPacket{}
|
||||
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 BenchmarkMarshalMsgmsgpRawPacket(b *testing.B) {
|
||||
v := msgpRawPacket{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgmsgpRawPacket(b *testing.B) {
|
||||
v := msgpRawPacket{}
|
||||
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 BenchmarkUnmarshalmsgpRawPacket(b *testing.B) {
|
||||
v := msgpRawPacket{}
|
||||
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 TestEncodeDecodemsgpRawPacket(t *testing.T) {
|
||||
v := msgpRawPacket{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodemsgpRawPacket Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := msgpRawPacket{}
|
||||
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 BenchmarkEncodemsgpRawPacket(b *testing.B) {
|
||||
v := msgpRawPacket{}
|
||||
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 BenchmarkDecodemsgpRawPacket(b *testing.B) {
|
||||
v := msgpRawPacket{}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -84,7 +84,9 @@ func (p *{{$structName}}) String() string {
|
|||
package skylab
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"encoding/binary"
|
||||
"github.com/kschamplin/gotelem/internal/can"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
@ -96,37 +98,42 @@ const (
|
|||
{{- end}}
|
||||
)
|
||||
|
||||
var nameToIdMap = map[string]can.CanID{
|
||||
|
||||
}
|
||||
|
||||
// list of every packet ID. can be used for O(1) checks.
|
||||
var idMap = map[uint32]bool{
|
||||
var idMap = map[can.CanID]bool{
|
||||
{{ range $p := .Packets -}}
|
||||
{{ if $p.Repeat }}
|
||||
{{ range $idx := Nx (int $p.Id) $p.Repeat $p.Offset -}}
|
||||
{{ $idx | printf "0x%X"}}: true,
|
||||
can.CanID{ Id: {{ $idx | printf "0x%X"}}, Extended: {{$p.Extended}} }: true,
|
||||
{{ end }}
|
||||
{{- else }}
|
||||
{{ $p.Id | printf "0x%X" }}: true,
|
||||
can.CanID{ Id: {{ $p.Id | printf "0x%X" }}, Extended: {{$p.Extended}} }: true,
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
}
|
||||
|
||||
// FromCanFrame creates a Packet from a given CAN ID and data payload.
|
||||
// If the CAN ID is unknown, it will return an error.
|
||||
func FromCanFrame(id uint32, data []byte) (Packet, error) {
|
||||
func FromCanFrame(f can.Frame) (Packet, error) {
|
||||
id := f.Id
|
||||
if !idMap[id] {
|
||||
return nil, &UnknownIdError{ id }
|
||||
return nil, &UnknownIdError{ id.Id }
|
||||
}
|
||||
switch id {
|
||||
{{- range $p := .Packets }}
|
||||
{{- if $p.Repeat }}
|
||||
case {{ Nx (int $p.Id) $p.Repeat $p.Offset | mapf "0x%X" | strJoin ", " -}}:
|
||||
case {{ $p | idToString -}}:
|
||||
var res = &{{camelCase $p.Name true}}{}
|
||||
res.UnmarshalPacket(data)
|
||||
res.Idx = id - {{$p.Id | printf "0x%X" }}
|
||||
res.UnmarshalPacket(f.Data)
|
||||
res.Idx = id.Id - {{$p.Id | printf "0x%X" }}
|
||||
return res, nil
|
||||
{{- else }}
|
||||
case {{ $p.Id | printf "0x%X" }}:
|
||||
case {{ $p | idToString }}:
|
||||
var res = &{{camelCase $p.Name true}}{}
|
||||
res.UnmarshalPacket(data)
|
||||
res.UnmarshalPacket(f.Data)
|
||||
return res, nil
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
|
@ -136,28 +143,18 @@ func FromCanFrame(id uint32, data []byte) (Packet, error) {
|
|||
}
|
||||
|
||||
|
||||
func FromJson (id uint32, raw []byte) (Packet, error) {
|
||||
if !idMap[id] {
|
||||
return nil, &UnknownIdError{ id }
|
||||
}
|
||||
switch id {
|
||||
func FromJson (name string, raw []byte) (Packet, error) {
|
||||
switch name {
|
||||
{{- range $p := .Packets }}
|
||||
{{- if $p.Repeat }}
|
||||
case {{ Nx (int $p.Id) $p.Repeat $p.Offset | mapf "0x%X" | strJoin ", " -}}:
|
||||
var res = &{{camelCase $p.Name true}}{}
|
||||
err := json.Unmarshal(raw, res)
|
||||
res.Idx = id - {{ $p.Id | printf "0x%X" }}
|
||||
return res, err
|
||||
{{- else }}
|
||||
case {{ $p.Id | printf "0x%X" }}:
|
||||
case "{{ $p.Name }}":
|
||||
var res = &{{camelCase $p.Name true}}{}
|
||||
err := json.Unmarshal(raw, res)
|
||||
return res, err
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
panic("This should never happen. CAN ID didn't match but was in ID map")
|
||||
return nil, errors.New("unknown packet name")
|
||||
|
||||
}
|
||||
|
||||
{{range .Packets -}}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/kschamplin/gotelem"
|
||||
"github.com/kschamplin/gotelem/internal/can"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
|
@ -128,21 +128,22 @@ func (sck *CanSocket) SetFilters(filters []CanFilter) error {
|
|||
}
|
||||
|
||||
// Send sends a CAN frame
|
||||
func (sck *CanSocket) Send(msg *gotelem.Frame) error {
|
||||
func (sck *CanSocket) Send(msg *can.Frame) error {
|
||||
|
||||
buf := make([]byte, fdFrameSize)
|
||||
|
||||
idToWrite := msg.Id
|
||||
idToWrite := msg.Id.Id
|
||||
|
||||
switch msg.Kind {
|
||||
case gotelem.CanSFFFrame:
|
||||
idToWrite &= unix.CAN_SFF_MASK
|
||||
case gotelem.CanEFFFrame:
|
||||
if (msg.Id.Extended) {
|
||||
idToWrite &= unix.CAN_EFF_MASK
|
||||
idToWrite |= unix.CAN_EFF_FLAG
|
||||
case gotelem.CanRTRFrame:
|
||||
}
|
||||
|
||||
|
||||
switch msg.Kind {
|
||||
case can.CanRTRFrame:
|
||||
idToWrite |= unix.CAN_RTR_FLAG
|
||||
case gotelem.CanErrFrame:
|
||||
case can.CanErrFrame:
|
||||
return errors.New("you can't send error frames")
|
||||
default:
|
||||
return errors.New("unknown frame type")
|
||||
|
@ -175,7 +176,7 @@ func (sck *CanSocket) Send(msg *gotelem.Frame) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (sck *CanSocket) Recv() (*gotelem.Frame, error) {
|
||||
func (sck *CanSocket) Recv() (*can.Frame, error) {
|
||||
|
||||
// todo: support extended frames.
|
||||
buf := make([]byte, fdFrameSize)
|
||||
|
@ -184,25 +185,33 @@ func (sck *CanSocket) Recv() (*gotelem.Frame, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
id := binary.LittleEndian.Uint32(buf[0:4])
|
||||
raw_id := binary.LittleEndian.Uint32(buf[0:4])
|
||||
|
||||
var k gotelem.Kind
|
||||
if id&unix.CAN_EFF_FLAG != 0 {
|
||||
var id can.CanID
|
||||
id.Id = raw_id
|
||||
if raw_id&unix.CAN_EFF_FLAG != 0 {
|
||||
// extended id frame
|
||||
k = gotelem.CanEFFFrame
|
||||
id.Extended = true;
|
||||
} else {
|
||||
// it's a normal can frame
|
||||
k = gotelem.CanSFFFrame
|
||||
id.Extended = false;
|
||||
}
|
||||
|
||||
if id&unix.CAN_ERR_FLAG != 0 {
|
||||
var k can.Kind = can.CanDataFrame
|
||||
|
||||
if raw_id&unix.CAN_ERR_FLAG != 0 {
|
||||
// we got an error...
|
||||
k = can.CanErrFrame
|
||||
}
|
||||
|
||||
if raw_id & unix.CAN_RTR_FLAG != 0 {
|
||||
k = can.CanRTRFrame
|
||||
}
|
||||
|
||||
dataLength := uint8(buf[4])
|
||||
|
||||
result := &gotelem.Frame{
|
||||
Id: id & unix.CAN_EFF_MASK,
|
||||
result := &can.Frame{
|
||||
Id: id,
|
||||
Kind: k,
|
||||
Data: buf[8 : dataLength+8],
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue