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"
|
"fmt"
|
||||||
|
|
||||||
"github.com/kschamplin/gotelem"
|
"github.com/kschamplin/gotelem"
|
||||||
"github.com/kschamplin/gotelem/mprpc"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,8 +34,3 @@ func client(ctx *cli.Context) error {
|
||||||
return nil
|
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"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/kschamplin/gotelem/internal/can"
|
||||||
"github.com/kschamplin/gotelem/skylab"
|
"github.com/kschamplin/gotelem/skylab"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"golang.org/x/exp/slog"
|
"golang.org/x/exp/slog"
|
||||||
|
@ -50,15 +52,164 @@ required for piping candump into skylabify. Likewise, data should be stored with
|
||||||
Name: "verbose",
|
Name: "verbose",
|
||||||
Aliases: []string{"v"},
|
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.Action = run
|
||||||
app.HideHelp = true
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
if err := app.Run(os.Args); err != nil {
|
||||||
panic(err)
|
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) {
|
func run(ctx *cli.Context) (err error) {
|
||||||
path := ctx.Args().Get(0)
|
path := ctx.Args().Get(0)
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
@ -66,7 +217,6 @@ func run(ctx *cli.Context) (err error) {
|
||||||
cli.ShowAppHelpAndExit(ctx, int(syscall.EINVAL))
|
cli.ShowAppHelpAndExit(ctx, int(syscall.EINVAL))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var istream *os.File
|
var istream *os.File
|
||||||
if path == "-" {
|
if path == "-" {
|
||||||
istream = os.Stdin
|
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 {
|
for {
|
||||||
// dumpline looks like this:
|
line, err := fileReader.ReadString('\n')
|
||||||
// (1684538768.521889) can0 200#8D643546
|
|
||||||
dumpLine, err := canDumpReader.ReadString('\n')
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, io.EOF) {
|
if errors.Is(err, io.EOF) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err // i/o failures are fatal
|
||||||
}
|
}
|
||||||
// remove trailing newline
|
f, err := pfun(line)
|
||||||
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)
|
|
||||||
var idErr *skylab.UnknownIdError
|
var idErr *skylab.UnknownIdError
|
||||||
if errors.As(err, &idErr) {
|
if errors.As(err, &idErr) {
|
||||||
// unknown id
|
// unknown id
|
||||||
slog.Info("unknown id", "err", err)
|
slog.Info("unknown id", "err", err)
|
||||||
|
unknown_packets++
|
||||||
continue
|
continue
|
||||||
} else if err != nil {
|
} 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.
|
// format and print out the JSON.
|
||||||
out, _ := json.Marshal(&cd)
|
out, _ := json.Marshal(&f)
|
||||||
fmt.Println(string(out))
|
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.
|
// this API only accepts JSON.
|
||||||
r.Use(middleware.AllowContentType("application/json"))
|
r.Use(middleware.AllowContentType("application/json"))
|
||||||
// no caching - always get the latest data.
|
// 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.Use(middleware.NoCache)
|
||||||
|
|
||||||
r.Get("/schema", func(w http.ResponseWriter, r *http.Request) {
|
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...)
|
db.AddEvents(pkgs...)
|
||||||
})
|
})
|
||||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
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.
|
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 {
|
func apiV1PacketSubscribe(broker *Broker, db *db.TelemDb) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
conn_id := r.RemoteAddr + uuid.New().String()
|
conn_id := r.RemoteAddr + uuid.New().String()
|
||||||
|
|
|
@ -2,20 +2,27 @@
|
||||||
//
|
//
|
||||||
// It has a generic can Frame (packet), as well as a filter type.
|
// It has a generic can Frame (packet), as well as a filter type.
|
||||||
// we also define standard interfaces for objects that can accept
|
// 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
|
// can frames. We can use this pattern to easily extend the capabilities of the program
|
||||||
// by writing "adapters" to various devices/formats (xbee, sqlite, network socket, socketcan)
|
// by writing "adapters" to various devices/formats (xbee, socketcan)
|
||||||
package gotelem
|
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,
|
// Frame represents a protocol-agnostic CAN frame. The Id can be standard or extended,
|
||||||
// but if it is extended, the Kind should be EFF.
|
// but if it is extended, the Kind should be EFF.
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
Id uint32
|
Id CanID
|
||||||
Data []byte
|
Data []byte
|
||||||
Kind Kind
|
Kind Kind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: should this be replaced
|
||||||
type CANFrame interface {
|
type CANFrame interface {
|
||||||
Id() uint32
|
Id()
|
||||||
Data() []byte
|
Data() []byte
|
||||||
Type() Kind
|
Type() Kind
|
||||||
}
|
}
|
||||||
|
@ -26,8 +33,7 @@ type CANFrame interface {
|
||||||
type Kind uint8
|
type Kind uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CanSFFFrame Kind = iota // Standard ID Frame
|
CanDataFrame Kind = iota // Standard ID Frame
|
||||||
CanEFFFrame // Extended ID Frame
|
|
||||||
CanRTRFrame // Remote Transmission Request Frame
|
CanRTRFrame // Remote Transmission Request Frame
|
||||||
CanErrFrame // Error Frame
|
CanErrFrame // Error Frame
|
||||||
)
|
)
|
|
@ -49,7 +49,7 @@ func OpenTelemDb(path string, options ...TelemDbOption) (tdb *TelemDb, err error
|
||||||
return
|
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)
|
fmt.Printf("starting version %d\n", version)
|
||||||
|
|
||||||
version, err = RunMigrations(tdb)
|
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
|
// sql expression to insert a bus event into the packets database.1
|
||||||
const sqlInsertEvent = `
|
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.
|
// AddEvent adds the bus event to the database.
|
||||||
|
@ -84,7 +84,12 @@ func (tdb *TelemDb) AddEventsCtx(ctx context.Context, events ...skylab.BusEvent)
|
||||||
return
|
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
|
var j []byte
|
||||||
j, err = json.Marshal(b.Data)
|
j, err = json.Marshal(b.Data)
|
||||||
|
|
||||||
|
@ -92,14 +97,23 @@ func (tdb *TelemDb) AddEventsCtx(ctx context.Context, events ...skylab.BusEvent)
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return
|
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)
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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()
|
tx.Commit()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -109,6 +123,7 @@ func (tdb *TelemDb) AddEvents(events ...skylab.BusEvent) (err error) {
|
||||||
return tdb.AddEventsCtx(context.Background(), events...)
|
return tdb.AddEventsCtx(context.Background(), events...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Query fragment guide:
|
/// Query fragment guide:
|
||||||
/// We need to be able to easily construct safe(!) and meaningful queries programatically
|
/// 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.
|
/// 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
|
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,
|
// 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.
|
// use time.Unix(0,0) or time.Now() in start and end respectively.
|
||||||
type QueryTimeRange struct {
|
type QueryTimeRange struct {
|
||||||
|
@ -204,9 +198,9 @@ func (tdb *TelemDb) GetEvents(limit int, where ...QueryFrag) (events []skylab.Bu
|
||||||
|
|
||||||
BusEv := skylab.BusEvent{
|
BusEv := skylab.BusEvent{
|
||||||
Timestamp: time.UnixMilli(int64(ev.Timestamp)),
|
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!
|
// FIXME: this is slow!
|
||||||
events = append(events, BusEv)
|
events = append(events, BusEv)
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
CREATE TABLE "bus_events" (
|
CREATE TABLE "bus_events" (
|
||||||
"ts" INTEGER NOT NULL, -- timestamp, unix milliseconds
|
"ts" INTEGER NOT NULL, -- timestamp, unix milliseconds
|
||||||
"id" INTEGER NOT NULL, -- can ID
|
|
||||||
"name" TEXT NOT NULL, -- name of base packet
|
"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
|
"data" TEXT NOT NULL CHECK(json_valid(data)) -- JSON object describing the data, including index if any
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX "ids_timestamped" ON "bus_events" (
|
CREATE INDEX "ids_timestamped" ON "bus_events" (
|
||||||
"id",
|
"name",
|
||||||
"ts" DESC
|
"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.
|
// SkylabFile is a yaml file from skylab.
|
||||||
type SkylabFile struct {
|
type SkylabFile struct {
|
||||||
Packets []PacketDef `json:"packets"`
|
Packets []PacketDef `yaml:"packets"`
|
||||||
Boards []BoardDef `json:"boards"`
|
Boards []BoardDef `yaml:"boards"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BoardDef struct {
|
type BoardDef struct {
|
||||||
Name string `json:"name"`
|
Name string `yaml:"name"`
|
||||||
Transmit []string `json:"transmit"`
|
Transmit []string `yaml:"transmit"`
|
||||||
Receive []string `json:"receive"`
|
Receive []string `yaml:"receive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// data field.
|
// data field.
|
||||||
type FieldDef struct {
|
type FieldDef struct {
|
||||||
Name string `json:"name"`
|
Name string `yaml:"name"`
|
||||||
Type string `json:"type"`
|
Type string `yaml:"type"`
|
||||||
Units string `json:"units"`
|
Units string `yaml:"units"`
|
||||||
Conversion float32 `json:"conversion"`
|
Conversion float32 `yaml:"conversion"`
|
||||||
Bits []struct {
|
Bits []struct {
|
||||||
Name string `json:"name"`
|
Name string `yaml:"name"`
|
||||||
} `json:"bits"`
|
} `yaml:"bits"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// a PacketDef is a full can packet.
|
// a PacketDef is a full can packet.
|
||||||
type PacketDef struct {
|
type PacketDef struct {
|
||||||
Name string `json:"name"`
|
Name string `yaml:"name"`
|
||||||
Description string `json:"description"`
|
Description string `yaml:"description"`
|
||||||
Id uint32 `json:"id"`
|
Id uint32 `yaml:"id"`
|
||||||
Endian string `json:"endian"`
|
Endian string `yaml:"endian"`
|
||||||
Repeat int `json:"repeat"`
|
Extended bool `yaml:"is_extended"`
|
||||||
Offset int `json:"offset"`
|
Repeat int `yaml:"repeat"`
|
||||||
Data []FieldDef `json:"data"`
|
Offset int `yaml:"offset"`
|
||||||
|
Data []FieldDef `yaml:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to generate bitfield types.
|
// we need to generate bitfield types.
|
||||||
|
@ -273,6 +274,20 @@ func mapf(format string, els []int) []string {
|
||||||
return resp
|
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() {
|
func main() {
|
||||||
// read path as the first arg, glob it for yamls, read each yaml into a skylabFile.
|
// 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.
|
// then take each skylab file, put all the packets into one big array.
|
||||||
|
@ -309,15 +324,16 @@ func main() {
|
||||||
|
|
||||||
// we add any functions mapping we need here.
|
// we add any functions mapping we need here.
|
||||||
fnMap := template.FuncMap{
|
fnMap := template.FuncMap{
|
||||||
"camelCase": toCamelInitCase,
|
"camelCase": toCamelInitCase,
|
||||||
"Time": time.Now,
|
"Time": time.Now,
|
||||||
"N": N,
|
"N": N,
|
||||||
"Nx": Nx,
|
"Nx": Nx,
|
||||||
"int": uint32ToInt,
|
"int": uint32ToInt,
|
||||||
"strJoin": strJoin,
|
"strJoin": strJoin,
|
||||||
"mapf": mapf,
|
"mapf": mapf,
|
||||||
"maptype": MapType,
|
"maptype": MapType,
|
||||||
"json": json.Marshal,
|
"json": json.Marshal,
|
||||||
|
"idToString": idToString,
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl, err := template.New("golang.go.tmpl").Funcs(fnMap).ParseGlob("templates/*.go.tmpl")
|
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 ----
|
// ---- other wire encoding business ----
|
||||||
|
|
||||||
// internal structure for partially decoding json object.
|
// internal structure for partially decoding json object.
|
||||||
// includes
|
|
||||||
type RawJsonEvent struct {
|
type RawJsonEvent struct {
|
||||||
Timestamp int64 `json:"ts" db:"ts"`
|
Timestamp int64 `json:"ts" db:"ts"`
|
||||||
Id uint32 `json:"id"`
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Data json.RawMessage `json:"data"`
|
Data json.RawMessage `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BusEvent is a timestamped Skylab packet
|
// BusEvent is a timestamped Skylab packet - it contains
|
||||||
type BusEvent struct {
|
type BusEvent struct {
|
||||||
Timestamp time.Time `json:"ts"`
|
Timestamp time.Time `json:"ts"`
|
||||||
Id uint32 `json:"id"`
|
Name string `json:"id"`
|
||||||
Data Packet `json:"data"`
|
Data Packet `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,10 +101,10 @@ func (e BusEvent) MarshalJSON() (b []byte, err error) {
|
||||||
// create the underlying raw event
|
// create the underlying raw event
|
||||||
j := &RawJsonEvent{
|
j := &RawJsonEvent{
|
||||||
Timestamp: e.Timestamp.UnixMilli(),
|
Timestamp: e.Timestamp.UnixMilli(),
|
||||||
Id: e.Id,
|
Name: e.Name,
|
||||||
Name: e.Data.String(),
|
|
||||||
}
|
}
|
||||||
// now we use the magic Packet -> map[string]interface{} function
|
// 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)
|
j.Data, err = json.Marshal(e.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -126,42 +124,12 @@ func (e *BusEvent) UnmarshalJSON(b []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Timestamp = time.UnixMilli(j.Timestamp)
|
e.Timestamp = time.UnixMilli(j.Timestamp)
|
||||||
e.Id = j.Id
|
e.Name = j.Name
|
||||||
e.Data, err = FromJson(j.Id, j.Data)
|
e.Data, err = FromJson(j.Name, j.Data)
|
||||||
|
|
||||||
return err
|
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
|
// 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
|
// 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) {
|
func TestMarshalUnmarshalDashboardPedalPercentages(t *testing.T) {
|
||||||
v := &DashboardPedalPercentages{}
|
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) {
|
func TestMarshalUnmarshalVisionTurnSignalsCommand(t *testing.T) {
|
||||||
v := &VisionTurnSignalsCommand{}
|
v := &VisionTurnSignalsCommand{}
|
||||||
|
@ -1663,8 +1807,8 @@ func TestJSONTrackerData(t *testing.T) {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
func TestMarshalUnmarshalTritiumMotorDrive(t *testing.T) {
|
func TestMarshalUnmarshalTritiumMotorDriveL(t *testing.T) {
|
||||||
v := &TritiumMotorDrive{}
|
v := &TritiumMotorDriveL{}
|
||||||
bin, err := v.MarshalPacket()
|
bin, err := v.MarshalPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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)
|
rawData, err := json.Marshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1691,7 +1835,7 @@ func TestJSONTritiumMotorDrive(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch underlying := p.(type) {
|
switch underlying := p.(type) {
|
||||||
case *TritiumMotorDrive:
|
case *TritiumMotorDriveL:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
||||||
|
@ -1699,8 +1843,8 @@ func TestJSONTritiumMotorDrive(t *testing.T) {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
func TestMarshalUnmarshalTritiumMotorPower(t *testing.T) {
|
func TestMarshalUnmarshalTritiumMotorPowerL(t *testing.T) {
|
||||||
v := &TritiumMotorPower{}
|
v := &TritiumMotorPowerL{}
|
||||||
bin, err := v.MarshalPacket()
|
bin, err := v.MarshalPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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)
|
rawData, err := json.Marshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1727,7 +1871,7 @@ func TestJSONTritiumMotorPower(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch underlying := p.(type) {
|
switch underlying := p.(type) {
|
||||||
case *TritiumMotorPower:
|
case *TritiumMotorPowerL:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
||||||
|
@ -1735,8 +1879,8 @@ func TestJSONTritiumMotorPower(t *testing.T) {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
func TestMarshalUnmarshalTritiumReset(t *testing.T) {
|
func TestMarshalUnmarshalTritiumResetL(t *testing.T) {
|
||||||
v := &TritiumReset{}
|
v := &TritiumResetL{}
|
||||||
bin, err := v.MarshalPacket()
|
bin, err := v.MarshalPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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)
|
rawData, err := json.Marshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1763,7 +1907,115 @@ func TestJSONTritiumReset(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch underlying := p.(type) {
|
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
|
break
|
||||||
default:
|
default:
|
||||||
t.Fatalf("didn't match type: %T, %v", underlying, underlying)
|
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
|
package skylab
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"github.com/kschamplin/gotelem/internal/can"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -96,37 +98,42 @@ const (
|
||||||
{{- end}}
|
{{- end}}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var nameToIdMap = map[string]can.CanID{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// list of every packet ID. can be used for O(1) checks.
|
// 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 -}}
|
{{ range $p := .Packets -}}
|
||||||
{{ if $p.Repeat }}
|
{{ if $p.Repeat }}
|
||||||
{{ range $idx := Nx (int $p.Id) $p.Repeat $p.Offset -}}
|
{{ 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 }}
|
{{ end }}
|
||||||
{{- else }}
|
{{- else }}
|
||||||
{{ $p.Id | printf "0x%X" }}: true,
|
can.CanID{ Id: {{ $p.Id | printf "0x%X" }}, Extended: {{$p.Extended}} }: true,
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromCanFrame creates a Packet from a given CAN ID and data payload.
|
// FromCanFrame creates a Packet from a given CAN ID and data payload.
|
||||||
// If the CAN ID is unknown, it will return an error.
|
// 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] {
|
if !idMap[id] {
|
||||||
return nil, &UnknownIdError{ id }
|
return nil, &UnknownIdError{ id.Id }
|
||||||
}
|
}
|
||||||
switch id {
|
switch id {
|
||||||
{{- range $p := .Packets }}
|
{{- range $p := .Packets }}
|
||||||
{{- if $p.Repeat }}
|
{{- 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}}{}
|
var res = &{{camelCase $p.Name true}}{}
|
||||||
res.UnmarshalPacket(data)
|
res.UnmarshalPacket(f.Data)
|
||||||
res.Idx = id - {{$p.Id | printf "0x%X" }}
|
res.Idx = id.Id - {{$p.Id | printf "0x%X" }}
|
||||||
return res, nil
|
return res, nil
|
||||||
{{- else }}
|
{{- else }}
|
||||||
case {{ $p.Id | printf "0x%X" }}:
|
case {{ $p | idToString }}:
|
||||||
var res = &{{camelCase $p.Name true}}{}
|
var res = &{{camelCase $p.Name true}}{}
|
||||||
res.UnmarshalPacket(data)
|
res.UnmarshalPacket(f.Data)
|
||||||
return res, nil
|
return res, nil
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
@ -136,28 +143,18 @@ func FromCanFrame(id uint32, data []byte) (Packet, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func FromJson (id uint32, raw []byte) (Packet, error) {
|
func FromJson (name string, raw []byte) (Packet, error) {
|
||||||
if !idMap[id] {
|
switch name {
|
||||||
return nil, &UnknownIdError{ id }
|
|
||||||
}
|
|
||||||
switch id {
|
|
||||||
{{- range $p := .Packets }}
|
{{- range $p := .Packets }}
|
||||||
{{- if $p.Repeat }}
|
case "{{ $p.Name }}":
|
||||||
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" }}:
|
|
||||||
var res = &{{camelCase $p.Name true}}{}
|
var res = &{{camelCase $p.Name true}}{}
|
||||||
err := json.Unmarshal(raw, res)
|
err := json.Unmarshal(raw, res)
|
||||||
return res, err
|
return res, err
|
||||||
{{- end }}
|
|
||||||
{{- 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 -}}
|
{{range .Packets -}}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/kschamplin/gotelem"
|
"github.com/kschamplin/gotelem/internal/can"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -128,21 +128,22 @@ func (sck *CanSocket) SetFilters(filters []CanFilter) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send sends a CAN frame
|
// 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)
|
buf := make([]byte, fdFrameSize)
|
||||||
|
|
||||||
idToWrite := msg.Id
|
idToWrite := msg.Id.Id
|
||||||
|
|
||||||
switch msg.Kind {
|
if (msg.Id.Extended) {
|
||||||
case gotelem.CanSFFFrame:
|
|
||||||
idToWrite &= unix.CAN_SFF_MASK
|
|
||||||
case gotelem.CanEFFFrame:
|
|
||||||
idToWrite &= unix.CAN_EFF_MASK
|
idToWrite &= unix.CAN_EFF_MASK
|
||||||
idToWrite |= unix.CAN_EFF_FLAG
|
idToWrite |= unix.CAN_EFF_FLAG
|
||||||
case gotelem.CanRTRFrame:
|
}
|
||||||
|
|
||||||
|
|
||||||
|
switch msg.Kind {
|
||||||
|
case can.CanRTRFrame:
|
||||||
idToWrite |= unix.CAN_RTR_FLAG
|
idToWrite |= unix.CAN_RTR_FLAG
|
||||||
case gotelem.CanErrFrame:
|
case can.CanErrFrame:
|
||||||
return errors.New("you can't send error frames")
|
return errors.New("you can't send error frames")
|
||||||
default:
|
default:
|
||||||
return errors.New("unknown frame type")
|
return errors.New("unknown frame type")
|
||||||
|
@ -175,7 +176,7 @@ func (sck *CanSocket) Send(msg *gotelem.Frame) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sck *CanSocket) Recv() (*gotelem.Frame, error) {
|
func (sck *CanSocket) Recv() (*can.Frame, error) {
|
||||||
|
|
||||||
// todo: support extended frames.
|
// todo: support extended frames.
|
||||||
buf := make([]byte, fdFrameSize)
|
buf := make([]byte, fdFrameSize)
|
||||||
|
@ -184,25 +185,33 @@ func (sck *CanSocket) Recv() (*gotelem.Frame, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
id := binary.LittleEndian.Uint32(buf[0:4])
|
raw_id := binary.LittleEndian.Uint32(buf[0:4])
|
||||||
|
|
||||||
var k gotelem.Kind
|
var id can.CanID
|
||||||
if id&unix.CAN_EFF_FLAG != 0 {
|
id.Id = raw_id
|
||||||
|
if raw_id&unix.CAN_EFF_FLAG != 0 {
|
||||||
// extended id frame
|
// extended id frame
|
||||||
k = gotelem.CanEFFFrame
|
id.Extended = true;
|
||||||
} else {
|
} else {
|
||||||
// it's a normal can frame
|
// 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...
|
// we got an error...
|
||||||
|
k = can.CanErrFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw_id & unix.CAN_RTR_FLAG != 0 {
|
||||||
|
k = can.CanRTRFrame
|
||||||
}
|
}
|
||||||
|
|
||||||
dataLength := uint8(buf[4])
|
dataLength := uint8(buf[4])
|
||||||
|
|
||||||
result := &gotelem.Frame{
|
result := &can.Frame{
|
||||||
Id: id & unix.CAN_EFF_MASK,
|
Id: id,
|
||||||
Kind: k,
|
Kind: k,
|
||||||
Data: buf[8 : dataLength+8],
|
Data: buf[8 : dataLength+8],
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue