diff --git a/cmd/gotelem/cli/client.go b/cmd/gotelem/cli/client.go index d34d7e8..54caf77 100644 --- a/cmd/gotelem/cli/client.go +++ b/cmd/gotelem/cli/client.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/kschamplin/gotelem" - "github.com/kschamplin/gotelem/mprpc" "github.com/urfave/cli/v2" ) @@ -35,8 +34,3 @@ func client(ctx *cli.Context) error { return nil } -// the client should connect to a TCP server and listen to packets. -func CANFrameHandler(f *gotelem.Frame) (*mprpc.RPCEmpty, error) { - fmt.Printf("got frame, %v\n", f) - return nil, nil -} diff --git a/cmd/skylabify/skylabify.go b/cmd/skylabify/skylabify.go index dd91d40..fe30fef 100644 --- a/cmd/skylabify/skylabify.go +++ b/cmd/skylabify/skylabify.go @@ -8,11 +8,13 @@ import ( "fmt" "io" "os" + "regexp" "strconv" "strings" "syscall" "time" + "github.com/kschamplin/gotelem/internal/can" "github.com/kschamplin/gotelem/skylab" "github.com/urfave/cli/v2" "golang.org/x/exp/slog" @@ -50,22 +52,170 @@ required for piping candump into skylabify. Likewise, data should be stored with Name: "verbose", Aliases: []string{"v"}, }, + &cli.StringFlag{ + Name: "format", + Aliases: []string{"f"}, + Usage: "the format of the incoming data. One of 'telem', 'candump'", + }, } app.Action = run - app.HideHelp = true if err := app.Run(os.Args); err != nil { panic(err) } } +// A FormatError is an error when parsing a format. Typically we simply ignore +// these and move on, but they can optionally wrap another error that is fatal. +type FormatError struct { + msg string + err error +} + +func (e *FormatError) Error() string { + return fmt.Sprintf("%s:%s", e.msg, e.err.Error()) +} +func (e *FormatError) Unwrap() error { + return e.err +} + +func NewFormatError(msg string, err error) error { + return &FormatError{msg: msg, err: err} +} + +// A Parser takes a string containing one line of a particular log file +// and returns an associated skylab.BusEvent representing the packet. +// if no packet is found, an error is returned instead. +type ParserFunc func(string) (skylab.BusEvent, error) + +func parseCanDumpLine(dumpLine string) (b skylab.BusEvent, err error) { + // dumpline looks like this: + // (1684538768.521889) can0 200#8D643546 + // remove trailing newline + dumpLine = strings.TrimSpace(dumpLine) + segments := strings.Split(dumpLine, " ") + + var unixSeconds, unixMicros int64 + fmt.Sscanf(segments[0], "(%d.%d)", &unixSeconds, &unixMicros) + b.Timestamp = time.Unix(unixSeconds, unixMicros) + + // now we extract the remaining data: + hexes := strings.Split(segments[2], "#") // first portion is id, second is data + + id, err := strconv.ParseUint(hexes[0], 16, 64) + if err != nil { + err = NewFormatError("failed to parse id", err) + return + } + if (len(hexes[1]) % 2) != 0 { + err = NewFormatError("odd number of hex characters", nil) + return + } + rawData, err := hex.DecodeString(hexes[1]) + if err != nil { + err = NewFormatError("failed to decode hex data", err) + return + } + + frame := can.Frame{ + // TODO: fix extended ids. we assume not extended for now. + Id: can.CanID{Id: uint32(id), Extended: false}, + Data: rawData, + Kind: can.CanDataFrame, + } + + b.Data, err = skylab.FromCanFrame(frame) + + if err != nil { + err = NewFormatError("failed to parse can frame", err) + return + } + + // set the name + b.Name = b.Data.String() + + return +} + +func parseTelemLogLine(line string) (b skylab.BusEvent, err error) { + // strip trailng newline since we rely on it being gone + line = strings.TrimSpace(line) + // data is of the form + // 1698180835.318 0619D80564080EBE241 + // the second part there is 3 nibbles (12 bits, 3 hex chars) for can ID, + // the rest is data. + // this regex does the processing. + r := regexp.MustCompile(`^(\d+).(\d{3}) (\w{3})(\w+)$`) + + // these files tend to get corrupted. there are all kinds of nasties that can happen. + // defense against random panics + defer func() { + if r := recover(); r != nil { + err = NewFormatError("caught panic", nil) + } + }() + a := r.FindStringSubmatch(line) + if a == nil { + err = NewFormatError("no regex match", nil) + return + } + var unixSeconds, unixMillis int64 + // note that a contains 5 elements, the first being the full match. + // so we start from the second element + unixSeconds, err = strconv.ParseInt(a[1], 10, 0) + if err != nil { + err = NewFormatError("failed to parse unix seconds", err) + return + } + unixMillis, err = strconv.ParseInt(a[2], 10, 0) + if err != nil { + err = NewFormatError("failed to parse unix millis", err) + return + } + ts := time.Unix(unixSeconds, unixMillis*1e6) + + id, err := strconv.ParseUint(a[3], 16, 16) + if err != nil { + err = NewFormatError("failed to parse id", err) + return + } + + if len(a[4])%2 != 0 { + // odd hex chars, protect against a panic + err = NewFormatError("wrong amount of hex chars", nil) + } + rawData, err := hex.DecodeString(a[4]) + if err != nil { + err = NewFormatError("failed to parse hex data", err) + return + } + frame := can.Frame{ + Id: can.CanID{Id: uint32(id), Extended: false}, + Data: rawData, + Kind: can.CanDataFrame, + } + b.Timestamp = ts + b.Data, err = skylab.FromCanFrame(frame) + if err != nil { + err = NewFormatError("failed to parse can frame", err) + return + } + b.Name = b.Data.String() + + return +} + +var parseMap = map[string]ParserFunc{ + "telem": parseTelemLogLine, + "candump": parseCanDumpLine, +} + func run(ctx *cli.Context) (err error) { path := ctx.Args().Get(0) if path == "" { fmt.Println("missing input file") cli.ShowAppHelpAndExit(ctx, int(syscall.EINVAL)) } - var istream *os.File if path == "-" { @@ -77,57 +227,43 @@ func run(ctx *cli.Context) (err error) { } } - canDumpReader := bufio.NewReader(istream) + fileReader := bufio.NewReader(istream) + + var pfun ParserFunc + + pfun, ok := parseMap[ctx.String("format")] + if !ok { + fmt.Println("invalid format!") + cli.ShowAppHelpAndExit(ctx, int(syscall.EINVAL)) + } + + n_err := 0 + unknown_packets := 0 for { - // dumpline looks like this: - // (1684538768.521889) can0 200#8D643546 - dumpLine, err := canDumpReader.ReadString('\n') + line, err := fileReader.ReadString('\n') if err != nil { if errors.Is(err, io.EOF) { return nil } - return err + return err // i/o failures are fatal } - // remove trailing newline - dumpLine = strings.TrimSpace(dumpLine) - - segments := strings.Split(dumpLine, " ") - - var cd skylab.BusEvent - var unixSeconds, unixMicros int64 - fmt.Sscanf(segments[0], "(%d.%d)", &unixSeconds, &unixMicros) - cd.Timestamp = time.Unix(unixSeconds, unixMicros*1000) // the canlog does usec precision for the decimal part. - - // this is for the latter part, we need to split id/data - hexes := strings.Split(segments[2], "#") - - // get the id - id, err := strconv.ParseUint(hexes[0], 16, 64) - if err != nil { - return err - } - cd.Id = uint32(id) - - // get the data to a []byte - rawData, err := hex.DecodeString(hexes[1]) - if err != nil { - return err - } - - // parse the data []byte to a skylab packet - cd.Data, err = skylab.FromCanFrame(uint32(cd.Id), rawData) + f, err := pfun(line) var idErr *skylab.UnknownIdError if errors.As(err, &idErr) { // unknown id slog.Info("unknown id", "err", err) + unknown_packets++ continue } else if err != nil { - return err + // TODO: we should consider absorbing all errors. + fmt.Printf("got an error %v\n", err) + n_err++ + continue } // format and print out the JSON. - out, _ := json.Marshal(&cd) + out, _ := json.Marshal(&f) fmt.Println(string(out)) } diff --git a/http.go b/http.go index 12704f6..6fae3be 100644 --- a/http.go +++ b/http.go @@ -57,6 +57,7 @@ func apiV1(broker *Broker, db *db.TelemDb) chi.Router { // this API only accepts JSON. r.Use(middleware.AllowContentType("application/json")) // no caching - always get the latest data. + // TODO: add a smart short expiry cache for queries that take a while. r.Use(middleware.NoCache) r.Get("/schema", func(w http.ResponseWriter, r *http.Request) { @@ -78,7 +79,7 @@ func apiV1(broker *Broker, db *db.TelemDb) chi.Router { db.AddEvents(pkgs...) }) r.Get("/", func(w http.ResponseWriter, r *http.Request) { - // this should use http query params o return a list of packets. + // this should use http query params to return a list of packets. }) @@ -113,6 +114,7 @@ type apiV1Subscriber struct { idFilter []uint32 // list of Ids to subscribe to. If it's empty, subscribes to all. } +// this is a websocket stream. func apiV1PacketSubscribe(broker *Broker, db *db.TelemDb) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { conn_id := r.RemoteAddr + uuid.New().String() diff --git a/frame.go b/internal/can/frame.go similarity index 83% rename from frame.go rename to internal/can/frame.go index 1b68071..f432b27 100644 --- a/frame.go +++ b/internal/can/frame.go @@ -2,20 +2,27 @@ // // It has a generic can Frame (packet), as well as a filter type. // we also define standard interfaces for objects that can accept -// can frames. We can use this pattern to easily extend the capabiltiies of the program -// by writing "adapters" to various devices/formats (xbee, sqlite, network socket, socketcan) -package gotelem +// can frames. We can use this pattern to easily extend the capabilities of the program +// by writing "adapters" to various devices/formats (xbee, socketcan) +package can + +type CanID struct { + Id uint32 + Extended bool // since the id itself is not enough. +} // Frame represents a protocol-agnostic CAN frame. The Id can be standard or extended, // but if it is extended, the Kind should be EFF. type Frame struct { - Id uint32 + Id CanID Data []byte Kind Kind } + +// TODO: should this be replaced type CANFrame interface { - Id() uint32 + Id() Data() []byte Type() Kind } @@ -26,8 +33,7 @@ type CANFrame interface { type Kind uint8 const ( - CanSFFFrame Kind = iota // Standard ID Frame - CanEFFFrame // Extended ID Frame + CanDataFrame Kind = iota // Standard ID Frame CanRTRFrame // Remote Transmission Request Frame CanErrFrame // Error Frame ) diff --git a/internal/db/db.go b/internal/db/db.go index 0272ea3..867bc1a 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -49,7 +49,7 @@ func OpenTelemDb(path string, options ...TelemDbOption) (tdb *TelemDb, err error return } - // get latest version of migrations - then run the SQL in order. + // get latest version of migrations - then run the SQL in order to perform them fmt.Printf("starting version %d\n", version) version, err = RunMigrations(tdb) @@ -72,7 +72,7 @@ func (tdb *TelemDb) SetVersion(version int) error { // sql expression to insert a bus event into the packets database.1 const sqlInsertEvent = ` -INSERT INTO "bus_events" (ts, id, name, data) VALUES ($1, $2, $3, json($4)); +INSERT INTO "bus_events" (ts, name, data) VALUES ($1, $2, json($3)); ` // AddEvent adds the bus event to the database. @@ -84,7 +84,12 @@ func (tdb *TelemDb) AddEventsCtx(ctx context.Context, events ...skylab.BusEvent) return } - for _, b := range events { + sqlStmt := sqlInsertEvent + const rowSql = "(?, ?, json(?))" + inserts := make([]string, len(events)) + vals := []interface{}{} + for idx, b := range events { + inserts[idx] = rowSql var j []byte j, err = json.Marshal(b.Data) @@ -92,14 +97,23 @@ func (tdb *TelemDb) AddEventsCtx(ctx context.Context, events ...skylab.BusEvent) tx.Rollback() return } - _, err = tx.ExecContext(ctx, sqlInsertEvent, b.Timestamp.UnixMilli(), b.Id, b.Data.String(), j) - - if err != nil { - tx.Rollback() - return - } - + vals = append(vals, b.Timestamp.UnixMilli(), b.Data.String(), j) } + + // construct the full statement now + sqlStmt = sqlStmt + strings.Join(inserts, ",") + stmt, err := tx.PrepareContext(ctx, sqlStmt) + if err != nil { + tx.Rollback() + return + } + //TODO: log the number of rows modified/inserted + _, err = stmt.ExecContext(ctx, vals...) + if err != nil { + tx.Rollback() + return + } + tx.Commit() return } @@ -109,6 +123,7 @@ func (tdb *TelemDb) AddEvents(events ...skylab.BusEvent) (err error) { return tdb.AddEventsCtx(context.Background(), events...) } + /// Query fragment guide: /// We need to be able to easily construct safe(!) and meaningful queries programatically /// so we make some new types that can be turned into SQL fragments that go inside the where clause. @@ -121,27 +136,6 @@ type QueryFrag interface { Query() string } -// QueryIdRange represents a range of IDs to select for, inclusive. -type QueryIdRange struct { - Start uint32 - End uint32 -} - -func (q *QueryIdRange) Query() string { - return fmt.Sprintf("id BETWEEN %d AND %d", q.Start, q.End) -} - -// QueryIds selects for individual CAN ids -type QueryIds []uint32 - -func (q QueryIds) Query() string { - var idStrings []string - for _, id := range q { - idStrings = append(idStrings, strconv.FormatUint(uint64(id), 10)) - } - return fmt.Sprintf("id IN (%s)", strings.Join(idStrings, ",")) -} - // QueryTimeRange represents a query of a specific time range. For "before" or "after" queries, // use time.Unix(0,0) or time.Now() in start and end respectively. type QueryTimeRange struct { @@ -204,9 +198,9 @@ func (tdb *TelemDb) GetEvents(limit int, where ...QueryFrag) (events []skylab.Bu BusEv := skylab.BusEvent{ Timestamp: time.UnixMilli(int64(ev.Timestamp)), - Id: ev.Id, + Name: ev.Name, } - BusEv.Data, err = skylab.FromJson(ev.Id, ev.Data) + BusEv.Data, err = skylab.FromJson(ev.Name, ev.Data) // FIXME: this is slow! events = append(events, BusEv) diff --git a/internal/db/migrations/1_initial_up.sql b/internal/db/migrations/1_initial_up.sql index f699ab2..1e6357c 100644 --- a/internal/db/migrations/1_initial_up.sql +++ b/internal/db/migrations/1_initial_up.sql @@ -1,15 +1,14 @@ CREATE TABLE "bus_events" ( "ts" INTEGER NOT NULL, -- timestamp, unix milliseconds - "id" INTEGER NOT NULL, -- can ID "name" TEXT NOT NULL, -- name of base packet "data" TEXT NOT NULL CHECK(json_valid(data)) -- JSON object describing the data, including index if any ); CREATE INDEX "ids_timestamped" ON "bus_events" ( - "id", + "name", "ts" DESC ); CREATE INDEX "times" ON "bus_events" ( "ts" DESC -); \ No newline at end of file +); diff --git a/mprpc/rpc.go b/mprpc/rpc.go deleted file mode 100644 index 4a2249d..0000000 --- a/mprpc/rpc.go +++ /dev/null @@ -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 - } -} diff --git a/mprpc/rpc_msg.go b/mprpc/rpc_msg.go deleted file mode 100644 index b4071ab..0000000 --- a/mprpc/rpc_msg.go +++ /dev/null @@ -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 { -} diff --git a/mprpc/rpc_msg_gen.go b/mprpc/rpc_msg_gen.go deleted file mode 100644 index 32c5ec3..0000000 --- a/mprpc/rpc_msg_gen.go +++ /dev/null @@ -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 -} diff --git a/mprpc/rpc_msg_gen_test.go b/mprpc/rpc_msg_gen_test.go deleted file mode 100644 index aa6493a..0000000 --- a/mprpc/rpc_msg_gen_test.go +++ /dev/null @@ -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) - } - } -} diff --git a/mprpc/rpc_test.go b/mprpc/rpc_test.go deleted file mode 100644 index 967bbe9..0000000 --- a/mprpc/rpc_test.go +++ /dev/null @@ -1 +0,0 @@ -package mprpc_test diff --git a/mprpc/rpcconntrack.go b/mprpc/rpcconntrack.go deleted file mode 100644 index c2ad098..0000000 --- a/mprpc/rpcconntrack.go +++ /dev/null @@ -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 -} diff --git a/skylab/make_skylab.go b/skylab/make_skylab.go index b4a99f1..97ded63 100644 --- a/skylab/make_skylab.go +++ b/skylab/make_skylab.go @@ -18,36 +18,37 @@ import ( // SkylabFile is a yaml file from skylab. type SkylabFile struct { - Packets []PacketDef `json:"packets"` - Boards []BoardDef `json:"boards"` + Packets []PacketDef `yaml:"packets"` + Boards []BoardDef `yaml:"boards"` } type BoardDef struct { - Name string `json:"name"` - Transmit []string `json:"transmit"` - Receive []string `json:"receive"` + Name string `yaml:"name"` + Transmit []string `yaml:"transmit"` + Receive []string `yaml:"receive"` } // data field. type FieldDef struct { - Name string `json:"name"` - Type string `json:"type"` - Units string `json:"units"` - Conversion float32 `json:"conversion"` + Name string `yaml:"name"` + Type string `yaml:"type"` + Units string `yaml:"units"` + Conversion float32 `yaml:"conversion"` Bits []struct { - Name string `json:"name"` - } `json:"bits"` + Name string `yaml:"name"` + } `yaml:"bits"` } // a PacketDef is a full can packet. type PacketDef struct { - Name string `json:"name"` - Description string `json:"description"` - Id uint32 `json:"id"` - Endian string `json:"endian"` - Repeat int `json:"repeat"` - Offset int `json:"offset"` - Data []FieldDef `json:"data"` + Name string `yaml:"name"` + Description string `yaml:"description"` + Id uint32 `yaml:"id"` + Endian string `yaml:"endian"` + Extended bool `yaml:"is_extended"` + Repeat int `yaml:"repeat"` + Offset int `yaml:"offset"` + Data []FieldDef `yaml:"data"` } // we need to generate bitfield types. @@ -273,6 +274,20 @@ func mapf(format string, els []int) []string { return resp } +func idToString(p PacketDef) string { + if p.Repeat > 0 { + resp := make([]string, p.Repeat) + for idx := 0; idx < p.Repeat; idx++ { + resp[idx] = fmt.Sprintf("can.CanID{ Id: 0x%X, Extended: %t }", int(p.Id)+idx*p.Offset, p.Extended) + } + + return strings.Join(resp, ",") + + } else { + return fmt.Sprintf("can.CanID{ Id: 0x%X, Extended: %t }", p.Id, p.Extended) + } +} + func main() { // read path as the first arg, glob it for yamls, read each yaml into a skylabFile. // then take each skylab file, put all the packets into one big array. @@ -309,15 +324,16 @@ func main() { // we add any functions mapping we need here. fnMap := template.FuncMap{ - "camelCase": toCamelInitCase, - "Time": time.Now, - "N": N, - "Nx": Nx, - "int": uint32ToInt, - "strJoin": strJoin, - "mapf": mapf, - "maptype": MapType, - "json": json.Marshal, + "camelCase": toCamelInitCase, + "Time": time.Now, + "N": N, + "Nx": Nx, + "int": uint32ToInt, + "strJoin": strJoin, + "mapf": mapf, + "maptype": MapType, + "json": json.Marshal, + "idToString": idToString, } tmpl, err := template.New("golang.go.tmpl").Funcs(fnMap).ParseGlob("templates/*.go.tmpl") diff --git a/skylab/skylab.go b/skylab/skylab.go index ff2904e..62e6363 100644 --- a/skylab/skylab.go +++ b/skylab/skylab.go @@ -84,18 +84,16 @@ func ToCanFrame(p Packet) (id uint32, data []byte, err error) { // ---- other wire encoding business ---- // internal structure for partially decoding json object. -// includes type RawJsonEvent struct { Timestamp int64 `json:"ts" db:"ts"` - Id uint32 `json:"id"` Name string `json:"name"` Data json.RawMessage `json:"data"` } -// BusEvent is a timestamped Skylab packet +// BusEvent is a timestamped Skylab packet - it contains type BusEvent struct { Timestamp time.Time `json:"ts"` - Id uint32 `json:"id"` + Name string `json:"id"` Data Packet `json:"data"` } @@ -103,10 +101,10 @@ func (e BusEvent) MarshalJSON() (b []byte, err error) { // create the underlying raw event j := &RawJsonEvent{ Timestamp: e.Timestamp.UnixMilli(), - Id: e.Id, - Name: e.Data.String(), + Name: e.Name, } // now we use the magic Packet -> map[string]interface{} function + // FIXME: this uses reflection and isn't good for the economy j.Data, err = json.Marshal(e.Data) if err != nil { return nil, err @@ -126,42 +124,12 @@ func (e *BusEvent) UnmarshalJSON(b []byte) error { } e.Timestamp = time.UnixMilli(j.Timestamp) - e.Id = j.Id - e.Data, err = FromJson(j.Id, j.Data) + e.Name = j.Name + e.Data, err = FromJson(j.Name, j.Data) return err } -func (e *BusEvent) MarshalMsg(b []byte) ([]byte, error) { - - // we need to send the bytes as a []byte instead of - // an object like the JSON one (lose self-documenting) - data, err := e.Data.MarshalPacket() - if err != nil { - return nil, err - } - rawEv := &msgpRawEvent{ - Timestamp: uint32(e.Timestamp.UnixMilli()), - Id: uint32(e.Id), - Data: data, - } - - return rawEv.MarshalMsg(b) -} - -func (e *BusEvent) UnmarshalMsg(b []byte) ([]byte, error) { - rawEv := &msgpRawEvent{} - remain, err := rawEv.UnmarshalMsg(b) - if err != nil { - return remain, err - } - e.Timestamp = time.UnixMilli(int64(rawEv.Timestamp)) - e.Id = rawEv.Id - e.Data, err = FromCanFrame(rawEv.Id, rawEv.Data) - - return remain, err -} - // we need to be able to parse the JSON as well. this is done using the // generator since we can use the switch/case thing since it's the fastest diff --git a/skylab/skylab_gen.go b/skylab/skylab_gen.go index 894d990..0bc8504 100644 --- a/skylab/skylab_gen.go +++ b/skylab/skylab_gen.go @@ -1,9 +1,11 @@ -// generated by gen_skylab.go at 2023-06-30 07:17:44.496220436 -0500 CDT m=+0.002737106 DO NOT EDIT! +// generated by gen_skylab.go at 2024-02-12 01:42:40.427923631 -0600 CST m=+0.004005217 DO NOT EDIT! package skylab import ( + "errors" "encoding/binary" + "github.com/kschamplin/gotelem/internal/can" "encoding/json" ) @@ -19,8 +21,10 @@ const ( BmsCurrentlimitId SkylabId = 0x18 BmsFanInfoId SkylabId = 0x19 BmsSetMinFanSpeedId SkylabId = 0x1B - BmsModuleId SkylabId = 0x1C + BmsModuleId SkylabId = 0x40 BmsChargerResponseId SkylabId = 0x75 + ChassisIsolationFaultId SkylabId = 0x38 + BmsImdInfoId SkylabId = 0x37 DashboardPedalPercentagesId SkylabId = 0x290 CarStateId SkylabId = 0x291 DashboardPedalFaultId SkylabId = 0x292 @@ -31,6 +35,8 @@ const ( FlightComputerInternalStateId SkylabId = 0x29D PowerToDriveId SkylabId = 0x19E ArrayPowerId SkylabId = 0x19F + ArrayEnergyId SkylabId = 0x119 + ArrayEnergyResetId SkylabId = 0x120 VisionTurnSignalsCommandId SkylabId = 0x2B0 VisionBrakeLightsCommandId SkylabId = 0x2B1 VisionHeadlightsCommandId SkylabId = 0x2B2 @@ -38,7 +44,7 @@ const ( VisionArrayLatchesCommandId SkylabId = 0x2B4 VisionRearviewCommandId SkylabId = 0x2B5 TrackerEnableId SkylabId = 0x610 - DistanceTraveledId SkylabId = 0x200 + DistanceTraveledId SkylabId = 0x19D ChargerStateId SkylabId = 0x573 ChargerBmsRequestId SkylabId = 0x74 ChargerCurrentVoltageId SkylabId = 0x576 @@ -56,9 +62,12 @@ const ( SteeringHornId SkylabId = 0x242 ThunderstruckStatusMessageId SkylabId = 0x18EB2440 TrackerDataId SkylabId = 0x600 - TritiumMotorDriveId SkylabId = 0x121 - TritiumMotorPowerId SkylabId = 0x122 - TritiumResetId SkylabId = 0x123 + TritiumMotorDriveLId SkylabId = 0x121 + TritiumMotorPowerLId SkylabId = 0x122 + TritiumResetLId SkylabId = 0x123 + TritiumMotorDriveRId SkylabId = 0x161 + TritiumMotorPowerRId SkylabId = 0x162 + TritiumResetRId SkylabId = 0x163 BmsAhSetId SkylabId = 0x16 BmsWhSetId SkylabId = 0x17 BmsKillId SkylabId = 0x1A @@ -95,483 +104,523 @@ const ( WslSlipSpeedMeasurementId SkylabId = 0x117 ) +var nameToIdMap = map[string]can.CanID{ + +} + // list of every packet ID. can be used for O(1) checks. -var idMap = map[uint32]bool{ +var idMap = map[can.CanID]bool{ - 0x10: true, - 0x11: true, - 0x12: true, - 0x13: true, - 0x14: true, - 0x15: true, - 0x18: true, - 0x19: true, - 0x1B: true, - 0x1C: true, - 0x1D: true, - 0x1E: true, - 0x1F: true, - 0x20: true, - 0x21: true, - 0x22: true, - 0x23: true, - 0x24: true, - 0x25: true, - 0x26: true, - 0x27: true, - 0x28: true, - 0x29: true, - 0x2A: true, - 0x2B: true, - 0x2C: true, - 0x2D: true, - 0x2E: true, - 0x2F: true, - 0x30: true, - 0x31: true, - 0x32: true, - 0x33: true, - 0x34: true, - 0x35: true, - 0x36: true, - 0x37: true, - 0x38: true, - 0x39: true, - 0x3A: true, - 0x3B: true, - 0x3C: true, - 0x3D: true, - 0x3E: true, - 0x3F: true, + can.CanID{ Id: 0x10, Extended: false }: true, + can.CanID{ Id: 0x11, Extended: false }: true, + can.CanID{ Id: 0x12, Extended: false }: true, + can.CanID{ Id: 0x13, Extended: false }: true, + can.CanID{ Id: 0x14, Extended: false }: true, + can.CanID{ Id: 0x15, Extended: false }: true, + can.CanID{ Id: 0x18, Extended: false }: true, + can.CanID{ Id: 0x19, Extended: false }: true, + can.CanID{ Id: 0x1B, Extended: false }: true, + can.CanID{ Id: 0x40, Extended: false }: true, + can.CanID{ Id: 0x41, Extended: false }: true, + can.CanID{ Id: 0x42, Extended: false }: true, + can.CanID{ Id: 0x43, Extended: false }: true, + can.CanID{ Id: 0x44, Extended: false }: true, + can.CanID{ Id: 0x45, Extended: false }: true, + can.CanID{ Id: 0x46, Extended: false }: true, + can.CanID{ Id: 0x47, Extended: false }: true, + can.CanID{ Id: 0x48, Extended: false }: true, + can.CanID{ Id: 0x49, Extended: false }: true, + can.CanID{ Id: 0x4A, Extended: false }: true, + can.CanID{ Id: 0x4B, Extended: false }: true, + can.CanID{ Id: 0x4C, Extended: false }: true, + can.CanID{ Id: 0x4D, Extended: false }: true, + can.CanID{ Id: 0x4E, Extended: false }: true, + can.CanID{ Id: 0x4F, Extended: false }: true, + can.CanID{ Id: 0x50, Extended: false }: true, + can.CanID{ Id: 0x51, Extended: false }: true, + can.CanID{ Id: 0x52, Extended: false }: true, + can.CanID{ Id: 0x53, Extended: false }: true, + can.CanID{ Id: 0x54, Extended: false }: true, + can.CanID{ Id: 0x55, Extended: false }: true, + can.CanID{ Id: 0x56, Extended: false }: true, + can.CanID{ Id: 0x57, Extended: false }: true, + can.CanID{ Id: 0x58, Extended: false }: true, + can.CanID{ Id: 0x59, Extended: false }: true, + can.CanID{ Id: 0x5A, Extended: false }: true, + can.CanID{ Id: 0x5B, Extended: false }: true, + can.CanID{ Id: 0x5C, Extended: false }: true, + can.CanID{ Id: 0x5D, Extended: false }: true, + can.CanID{ Id: 0x5E, Extended: false }: true, + can.CanID{ Id: 0x5F, Extended: false }: true, + can.CanID{ Id: 0x60, Extended: false }: true, + can.CanID{ Id: 0x61, Extended: false }: true, + can.CanID{ Id: 0x62, Extended: false }: true, + can.CanID{ Id: 0x63, Extended: false }: true, - 0x75: true, - 0x290: true, - 0x291: true, - 0x292: true, - 0x299: true, - 0x29A: true, - 0x29B: true, - 0x29C: true, - 0x29D: true, - 0x19E: true, - 0x19F: true, - 0x2B0: true, - 0x2B1: true, - 0x2B2: true, - 0x2B3: true, - 0x2B4: true, - 0x2B5: true, - 0x610: true, - 0x611: true, - 0x612: true, - 0x613: true, - 0x614: true, - 0x615: true, + can.CanID{ Id: 0x75, Extended: false }: true, + can.CanID{ Id: 0x38, Extended: false }: true, + can.CanID{ Id: 0x37, Extended: false }: true, + can.CanID{ Id: 0x290, Extended: false }: true, + can.CanID{ Id: 0x291, Extended: false }: true, + can.CanID{ Id: 0x292, Extended: false }: true, + can.CanID{ Id: 0x299, Extended: false }: true, + can.CanID{ Id: 0x29A, Extended: false }: true, + can.CanID{ Id: 0x29B, Extended: false }: true, + can.CanID{ Id: 0x29C, Extended: false }: true, + can.CanID{ Id: 0x29D, Extended: false }: true, + can.CanID{ Id: 0x19E, Extended: false }: true, + can.CanID{ Id: 0x19F, Extended: false }: true, + can.CanID{ Id: 0x119, Extended: false }: true, + can.CanID{ Id: 0x120, Extended: false }: true, + can.CanID{ Id: 0x2B0, Extended: false }: true, + can.CanID{ Id: 0x2B1, Extended: false }: true, + can.CanID{ Id: 0x2B2, Extended: false }: true, + can.CanID{ Id: 0x2B3, Extended: false }: true, + can.CanID{ Id: 0x2B4, Extended: false }: true, + can.CanID{ Id: 0x2B5, Extended: false }: true, + can.CanID{ Id: 0x610, Extended: false }: true, + can.CanID{ Id: 0x611, Extended: false }: true, + can.CanID{ Id: 0x612, Extended: false }: true, + can.CanID{ Id: 0x613, Extended: false }: true, + can.CanID{ Id: 0x614, Extended: false }: true, + can.CanID{ Id: 0x615, Extended: false }: true, - 0x200: true, - 0x573: true, - 0x74: true, - 0x576: true, - 0x577: true, - 0x18E54024: true, - 0x2B6: true, - 0x2B7: true, - 0x300: true, - 0x301: true, - 0x302: true, - 0x240: true, - 0x250: true, - 0x241: true, - 0x251: true, - 0x242: true, - 0x18EB2440: true, - 0x600: true, - 0x601: true, - 0x602: true, - 0x603: true, - 0x604: true, - 0x605: true, + can.CanID{ Id: 0x19D, Extended: false }: true, + can.CanID{ Id: 0x573, Extended: false }: true, + can.CanID{ Id: 0x74, Extended: false }: true, + can.CanID{ Id: 0x576, Extended: false }: true, + can.CanID{ Id: 0x577, Extended: false }: true, + can.CanID{ Id: 0x18E54024, Extended: true }: true, + can.CanID{ Id: 0x2B6, Extended: false }: true, + can.CanID{ Id: 0x2B7, Extended: false }: true, + can.CanID{ Id: 0x300, Extended: false }: true, + can.CanID{ Id: 0x301, Extended: false }: true, + can.CanID{ Id: 0x302, Extended: false }: true, + can.CanID{ Id: 0x240, Extended: false }: true, + can.CanID{ Id: 0x250, Extended: false }: true, + can.CanID{ Id: 0x241, Extended: false }: true, + can.CanID{ Id: 0x251, Extended: false }: true, + can.CanID{ Id: 0x242, Extended: false }: true, + can.CanID{ Id: 0x18EB2440, Extended: true }: true, + can.CanID{ Id: 0x600, Extended: false }: true, + can.CanID{ Id: 0x601, Extended: false }: true, + can.CanID{ Id: 0x602, Extended: false }: true, + can.CanID{ Id: 0x603, Extended: false }: true, + can.CanID{ Id: 0x604, Extended: false }: true, + can.CanID{ Id: 0x605, Extended: false }: true, - 0x121: true, - 0x122: true, - 0x123: true, - 0x16: true, - 0x17: true, - 0x1A: true, - 0x700: true, - 0x140: true, - 0x141: true, - 0x142: true, - 0x143: true, - 0x144: true, - 0x145: true, - 0x146: true, - 0x147: true, - 0x148: true, - 0x149: true, - 0x14B: true, - 0x14C: true, - 0x14D: true, - 0x14E: true, - 0x157: true, - 0x100: true, - 0x101: true, - 0x102: true, - 0x103: true, - 0x104: true, - 0x105: true, - 0x106: true, - 0x107: true, - 0x108: true, - 0x109: true, - 0x10B: true, - 0x10C: true, - 0x10E: true, - 0x10D: true, - 0x117: true, + can.CanID{ Id: 0x121, Extended: false }: true, + can.CanID{ Id: 0x122, Extended: false }: true, + can.CanID{ Id: 0x123, Extended: false }: true, + can.CanID{ Id: 0x161, Extended: false }: true, + can.CanID{ Id: 0x162, Extended: false }: true, + can.CanID{ Id: 0x163, Extended: false }: true, + can.CanID{ Id: 0x16, Extended: false }: true, + can.CanID{ Id: 0x17, Extended: false }: true, + can.CanID{ Id: 0x1A, Extended: false }: true, + can.CanID{ Id: 0x700, Extended: false }: true, + can.CanID{ Id: 0x140, Extended: false }: true, + can.CanID{ Id: 0x141, Extended: false }: true, + can.CanID{ Id: 0x142, Extended: false }: true, + can.CanID{ Id: 0x143, Extended: false }: true, + can.CanID{ Id: 0x144, Extended: false }: true, + can.CanID{ Id: 0x145, Extended: false }: true, + can.CanID{ Id: 0x146, Extended: false }: true, + can.CanID{ Id: 0x147, Extended: false }: true, + can.CanID{ Id: 0x148, Extended: false }: true, + can.CanID{ Id: 0x149, Extended: false }: true, + can.CanID{ Id: 0x14B, Extended: false }: true, + can.CanID{ Id: 0x14C, Extended: false }: true, + can.CanID{ Id: 0x14D, Extended: false }: true, + can.CanID{ Id: 0x14E, Extended: false }: true, + can.CanID{ Id: 0x157, Extended: false }: true, + can.CanID{ Id: 0x100, Extended: false }: true, + can.CanID{ Id: 0x101, Extended: false }: true, + can.CanID{ Id: 0x102, Extended: false }: true, + can.CanID{ Id: 0x103, Extended: false }: true, + can.CanID{ Id: 0x104, Extended: false }: true, + can.CanID{ Id: 0x105, Extended: false }: true, + can.CanID{ Id: 0x106, Extended: false }: true, + can.CanID{ Id: 0x107, Extended: false }: true, + can.CanID{ Id: 0x108, Extended: false }: true, + can.CanID{ Id: 0x109, Extended: false }: true, + can.CanID{ Id: 0x10B, Extended: false }: true, + can.CanID{ Id: 0x10C, Extended: false }: true, + can.CanID{ Id: 0x10E, Extended: false }: true, + can.CanID{ Id: 0x10D, Extended: false }: true, + can.CanID{ Id: 0x117, Extended: false }: true, } // FromCanFrame creates a Packet from a given CAN ID and data payload. // If the CAN ID is unknown, it will return an error. -func FromCanFrame(id uint32, data []byte) (Packet, error) { +func FromCanFrame(f can.Frame) (Packet, error) { + id := f.Id if !idMap[id] { - return nil, &UnknownIdError{ id } + return nil, &UnknownIdError{ id.Id } } switch id { - case 0x10: + case can.CanID{ Id: 0x10, Extended: false }: var res = &BmsMeasurement{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x11: + case can.CanID{ Id: 0x11, Extended: false }: var res = &BatteryStatus{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x12: + case can.CanID{ Id: 0x12, Extended: false }: var res = &BmsKillReason{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x13: + case can.CanID{ Id: 0x13, Extended: false }: var res = &BmsModuleMinMax{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x14: + case can.CanID{ Id: 0x14, Extended: false }: var res = &BmsSoc{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x15: + case can.CanID{ Id: 0x15, Extended: false }: var res = &BmsCapacity{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x18: + case can.CanID{ Id: 0x18, Extended: false }: var res = &BmsCurrentlimit{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x19: + case can.CanID{ Id: 0x19, Extended: false }: var res = &BmsFanInfo{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x1B: + case can.CanID{ Id: 0x1B, Extended: false }: var res = &BmsSetMinFanSpeed{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F: + case can.CanID{ Id: 0x40, Extended: false },can.CanID{ Id: 0x41, Extended: false },can.CanID{ Id: 0x42, Extended: false },can.CanID{ Id: 0x43, Extended: false },can.CanID{ Id: 0x44, Extended: false },can.CanID{ Id: 0x45, Extended: false },can.CanID{ Id: 0x46, Extended: false },can.CanID{ Id: 0x47, Extended: false },can.CanID{ Id: 0x48, Extended: false },can.CanID{ Id: 0x49, Extended: false },can.CanID{ Id: 0x4A, Extended: false },can.CanID{ Id: 0x4B, Extended: false },can.CanID{ Id: 0x4C, Extended: false },can.CanID{ Id: 0x4D, Extended: false },can.CanID{ Id: 0x4E, Extended: false },can.CanID{ Id: 0x4F, Extended: false },can.CanID{ Id: 0x50, Extended: false },can.CanID{ Id: 0x51, Extended: false },can.CanID{ Id: 0x52, Extended: false },can.CanID{ Id: 0x53, Extended: false },can.CanID{ Id: 0x54, Extended: false },can.CanID{ Id: 0x55, Extended: false },can.CanID{ Id: 0x56, Extended: false },can.CanID{ Id: 0x57, Extended: false },can.CanID{ Id: 0x58, Extended: false },can.CanID{ Id: 0x59, Extended: false },can.CanID{ Id: 0x5A, Extended: false },can.CanID{ Id: 0x5B, Extended: false },can.CanID{ Id: 0x5C, Extended: false },can.CanID{ Id: 0x5D, Extended: false },can.CanID{ Id: 0x5E, Extended: false },can.CanID{ Id: 0x5F, Extended: false },can.CanID{ Id: 0x60, Extended: false },can.CanID{ Id: 0x61, Extended: false },can.CanID{ Id: 0x62, Extended: false },can.CanID{ Id: 0x63, Extended: false }: var res = &BmsModule{} - res.UnmarshalPacket(data) - res.Idx = id - 0x1C + res.UnmarshalPacket(f.Data) + res.Idx = id.Id - 0x40 return res, nil - case 0x75: + case can.CanID{ Id: 0x75, Extended: false }: var res = &BmsChargerResponse{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x290: + case can.CanID{ Id: 0x38, Extended: false }: + var res = &ChassisIsolationFault{} + res.UnmarshalPacket(f.Data) + return res, nil + case can.CanID{ Id: 0x37, Extended: false }: + var res = &BmsImdInfo{} + res.UnmarshalPacket(f.Data) + return res, nil + case can.CanID{ Id: 0x290, Extended: false }: var res = &DashboardPedalPercentages{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x291: + case can.CanID{ Id: 0x291, Extended: false }: var res = &CarState{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x292: + case can.CanID{ Id: 0x292, Extended: false }: var res = &DashboardPedalFault{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x299: + case can.CanID{ Id: 0x299, Extended: false }: var res = &DashboardSystemTimeoutTest{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x29A: + case can.CanID{ Id: 0x29A, Extended: false }: var res = &CarSpeed{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x29B: + case can.CanID{ Id: 0x29B, Extended: false }: var res = &FlightComputerLvBoardDisconnectCounts{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x29C: + case can.CanID{ Id: 0x29C, Extended: false }: var res = &FlightComputerHvBoardDisconnectCounts{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x29D: + case can.CanID{ Id: 0x29D, Extended: false }: var res = &FlightComputerInternalState{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x19E: + case can.CanID{ Id: 0x19E, Extended: false }: var res = &PowerToDrive{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x19F: + case can.CanID{ Id: 0x19F, Extended: false }: var res = &ArrayPower{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x2B0: + case can.CanID{ Id: 0x119, Extended: false }: + var res = &ArrayEnergy{} + res.UnmarshalPacket(f.Data) + return res, nil + case can.CanID{ Id: 0x120, Extended: false }: + var res = &ArrayEnergyReset{} + res.UnmarshalPacket(f.Data) + return res, nil + case can.CanID{ Id: 0x2B0, Extended: false }: var res = &VisionTurnSignalsCommand{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x2B1: + case can.CanID{ Id: 0x2B1, Extended: false }: var res = &VisionBrakeLightsCommand{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x2B2: + case can.CanID{ Id: 0x2B2, Extended: false }: var res = &VisionHeadlightsCommand{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x2B3: + case can.CanID{ Id: 0x2B3, Extended: false }: var res = &VisionHornCommand{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x2B4: + case can.CanID{ Id: 0x2B4, Extended: false }: var res = &VisionArrayLatchesCommand{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x2B5: + case can.CanID{ Id: 0x2B5, Extended: false }: var res = &VisionRearviewCommand{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x610, 0x611, 0x612, 0x613, 0x614, 0x615: + case can.CanID{ Id: 0x610, Extended: false },can.CanID{ Id: 0x611, Extended: false },can.CanID{ Id: 0x612, Extended: false },can.CanID{ Id: 0x613, Extended: false },can.CanID{ Id: 0x614, Extended: false },can.CanID{ Id: 0x615, Extended: false }: var res = &TrackerEnable{} - res.UnmarshalPacket(data) - res.Idx = id - 0x610 + res.UnmarshalPacket(f.Data) + res.Idx = id.Id - 0x610 return res, nil - case 0x200: + case can.CanID{ Id: 0x19D, Extended: false }: var res = &DistanceTraveled{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x573: + case can.CanID{ Id: 0x573, Extended: false }: var res = &ChargerState{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x74: + case can.CanID{ Id: 0x74, Extended: false }: var res = &ChargerBmsRequest{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x576: + case can.CanID{ Id: 0x576, Extended: false }: var res = &ChargerCurrentVoltage{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x577: + case can.CanID{ Id: 0x577, Extended: false }: var res = &ChargerPower{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x18E54024: + case can.CanID{ Id: 0x18E54024, Extended: true }: var res = &ThunderstruckControlMessage{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x2B6: + case can.CanID{ Id: 0x2B6, Extended: false }: var res = &VisionStatusFront{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x2B7: + case can.CanID{ Id: 0x2B7, Extended: false }: var res = &VisionStatusRear{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x300: + case can.CanID{ Id: 0x300, Extended: false }: var res = &LightsFrontId{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x301: + case can.CanID{ Id: 0x301, Extended: false }: var res = &LightsBackId{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x302: + case can.CanID{ Id: 0x302, Extended: false }: var res = &VisionId{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x240: + case can.CanID{ Id: 0x240, Extended: false }: var res = &SteeringPressCount1{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x250: + case can.CanID{ Id: 0x250, Extended: false }: var res = &SteeringPressCount2{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x241: + case can.CanID{ Id: 0x241, Extended: false }: var res = &SteeringButtonColors1{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x251: + case can.CanID{ Id: 0x251, Extended: false }: var res = &SteeringButtonColors2{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x242: + case can.CanID{ Id: 0x242, Extended: false }: var res = &SteeringHorn{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x18EB2440: + case can.CanID{ Id: 0x18EB2440, Extended: true }: var res = &ThunderstruckStatusMessage{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x600, 0x601, 0x602, 0x603, 0x604, 0x605: + case can.CanID{ Id: 0x600, Extended: false },can.CanID{ Id: 0x601, Extended: false },can.CanID{ Id: 0x602, Extended: false },can.CanID{ Id: 0x603, Extended: false },can.CanID{ Id: 0x604, Extended: false },can.CanID{ Id: 0x605, Extended: false }: var res = &TrackerData{} - res.UnmarshalPacket(data) - res.Idx = id - 0x600 + res.UnmarshalPacket(f.Data) + res.Idx = id.Id - 0x600 return res, nil - case 0x121: - var res = &TritiumMotorDrive{} - res.UnmarshalPacket(data) + case can.CanID{ Id: 0x121, Extended: false }: + var res = &TritiumMotorDriveL{} + res.UnmarshalPacket(f.Data) return res, nil - case 0x122: - var res = &TritiumMotorPower{} - res.UnmarshalPacket(data) + case can.CanID{ Id: 0x122, Extended: false }: + var res = &TritiumMotorPowerL{} + res.UnmarshalPacket(f.Data) return res, nil - case 0x123: - var res = &TritiumReset{} - res.UnmarshalPacket(data) + case can.CanID{ Id: 0x123, Extended: false }: + var res = &TritiumResetL{} + res.UnmarshalPacket(f.Data) return res, nil - case 0x16: + case can.CanID{ Id: 0x161, Extended: false }: + var res = &TritiumMotorDriveR{} + res.UnmarshalPacket(f.Data) + return res, nil + case can.CanID{ Id: 0x162, Extended: false }: + var res = &TritiumMotorPowerR{} + res.UnmarshalPacket(f.Data) + return res, nil + case can.CanID{ Id: 0x163, Extended: false }: + var res = &TritiumResetR{} + res.UnmarshalPacket(f.Data) + return res, nil + case can.CanID{ Id: 0x16, Extended: false }: var res = &BmsAhSet{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x17: + case can.CanID{ Id: 0x17, Extended: false }: var res = &BmsWhSet{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x1A: + case can.CanID{ Id: 0x1A, Extended: false }: var res = &BmsKill{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x700: + case can.CanID{ Id: 0x700, Extended: false }: var res = &TelemetryRtcReset{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x140: + case can.CanID{ Id: 0x140, Extended: false }: var res = &WsrIdentification{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x141: + case can.CanID{ Id: 0x141, Extended: false }: var res = &WsrStatusInformation{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x142: + case can.CanID{ Id: 0x142, Extended: false }: var res = &WsrBusMeasurement{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x143: + case can.CanID{ Id: 0x143, Extended: false }: var res = &WsrVelocity{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x144: + case can.CanID{ Id: 0x144, Extended: false }: var res = &WsrPhaseCurrent{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x145: + case can.CanID{ Id: 0x145, Extended: false }: var res = &WsrMotorVoltageVector{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x146: + case can.CanID{ Id: 0x146, Extended: false }: var res = &WsrMotorCurrentVector{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x147: + case can.CanID{ Id: 0x147, Extended: false }: var res = &WsrMotorBackemf{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x148: + case can.CanID{ Id: 0x148, Extended: false }: var res = &Wsr15165VoltageRail{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x149: + case can.CanID{ Id: 0x149, Extended: false }: var res = &Wsr2512VoltageRail{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x14B: + case can.CanID{ Id: 0x14B, Extended: false }: var res = &WsrHeatsinkMotorTemp{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x14C: + case can.CanID{ Id: 0x14C, Extended: false }: var res = &WsrDspBoardTemp{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x14D: + case can.CanID{ Id: 0x14D, Extended: false }: var res = &WsrReserved{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x14E: + case can.CanID{ Id: 0x14E, Extended: false }: var res = &WsrOdometerBusAmphoursMeasurement{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x157: + case can.CanID{ Id: 0x157, Extended: false }: var res = &WsrSlipSpeedMeasurement{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x100: + case can.CanID{ Id: 0x100, Extended: false }: var res = &WslIdentification{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x101: + case can.CanID{ Id: 0x101, Extended: false }: var res = &WslStatusInformation{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x102: + case can.CanID{ Id: 0x102, Extended: false }: var res = &WslBusMeasurement{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x103: + case can.CanID{ Id: 0x103, Extended: false }: var res = &WslVelocity{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x104: + case can.CanID{ Id: 0x104, Extended: false }: var res = &WslPhaseCurrent{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x105: + case can.CanID{ Id: 0x105, Extended: false }: var res = &WslMotorVoltageVector{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x106: + case can.CanID{ Id: 0x106, Extended: false }: var res = &WslMotorCurrentVector{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x107: + case can.CanID{ Id: 0x107, Extended: false }: var res = &WslMotorBackemf{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x108: + case can.CanID{ Id: 0x108, Extended: false }: var res = &Wsl15165VoltageRail{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x109: + case can.CanID{ Id: 0x109, Extended: false }: var res = &Wsl2512VoltageRail{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x10B: + case can.CanID{ Id: 0x10B, Extended: false }: var res = &WslHeatsinkMotorTemp{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x10C: + case can.CanID{ Id: 0x10C, Extended: false }: var res = &WslDspBoardTemp{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x10E: + case can.CanID{ Id: 0x10E, Extended: false }: var res = &WslOdometerBusAmphoursMeasurement{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x10D: + case can.CanID{ Id: 0x10D, Extended: false }: var res = &WslReserved{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil - case 0x117: + case can.CanID{ Id: 0x117, Extended: false }: var res = &WslSlipSpeedMeasurement{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil } @@ -579,349 +628,372 @@ func FromCanFrame(id uint32, data []byte) (Packet, error) { } -func FromJson (id uint32, raw []byte) (Packet, error) { - if !idMap[id] { - return nil, &UnknownIdError{ id } - } - switch id { - case 0x10: +func FromJson (name string, raw []byte) (Packet, error) { + switch name { + case "bms_measurement": var res = &BmsMeasurement{} err := json.Unmarshal(raw, res) return res, err - case 0x11: + case "battery_status": var res = &BatteryStatus{} err := json.Unmarshal(raw, res) return res, err - case 0x12: + case "bms_kill_reason": var res = &BmsKillReason{} err := json.Unmarshal(raw, res) return res, err - case 0x13: + case "bms_module_min_max": var res = &BmsModuleMinMax{} err := json.Unmarshal(raw, res) return res, err - case 0x14: + case "bms_soc": var res = &BmsSoc{} err := json.Unmarshal(raw, res) return res, err - case 0x15: + case "bms_capacity": var res = &BmsCapacity{} err := json.Unmarshal(raw, res) return res, err - case 0x18: + case "bms_currentlimit": var res = &BmsCurrentlimit{} err := json.Unmarshal(raw, res) return res, err - case 0x19: + case "bms_fan_info": var res = &BmsFanInfo{} err := json.Unmarshal(raw, res) return res, err - case 0x1B: + case "bms_set_min_fan_speed": var res = &BmsSetMinFanSpeed{} err := json.Unmarshal(raw, res) return res, err - case 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F: + case "bms_module": var res = &BmsModule{} err := json.Unmarshal(raw, res) - res.Idx = id - 0x1C return res, err - case 0x75: + case "bms_charger_response": var res = &BmsChargerResponse{} err := json.Unmarshal(raw, res) return res, err - case 0x290: + case "chassis_isolation_fault": + var res = &ChassisIsolationFault{} + err := json.Unmarshal(raw, res) + return res, err + case "bms_imd_info": + var res = &BmsImdInfo{} + err := json.Unmarshal(raw, res) + return res, err + case "dashboard_pedal_percentages": var res = &DashboardPedalPercentages{} err := json.Unmarshal(raw, res) return res, err - case 0x291: + case "car_state": var res = &CarState{} err := json.Unmarshal(raw, res) return res, err - case 0x292: + case "dashboard_pedal_fault": var res = &DashboardPedalFault{} err := json.Unmarshal(raw, res) return res, err - case 0x299: + case "dashboard_system_timeout_test": var res = &DashboardSystemTimeoutTest{} err := json.Unmarshal(raw, res) return res, err - case 0x29A: + case "car_speed": var res = &CarSpeed{} err := json.Unmarshal(raw, res) return res, err - case 0x29B: + case "flight_computer_lv_board_disconnect_counts": var res = &FlightComputerLvBoardDisconnectCounts{} err := json.Unmarshal(raw, res) return res, err - case 0x29C: + case "flight_computer_hv_board_disconnect_counts": var res = &FlightComputerHvBoardDisconnectCounts{} err := json.Unmarshal(raw, res) return res, err - case 0x29D: + case "flight_computer_internal_state": var res = &FlightComputerInternalState{} err := json.Unmarshal(raw, res) return res, err - case 0x19E: + case "power_to_drive": var res = &PowerToDrive{} err := json.Unmarshal(raw, res) return res, err - case 0x19F: + case "array_power": var res = &ArrayPower{} err := json.Unmarshal(raw, res) return res, err - case 0x2B0: + case "array_energy": + var res = &ArrayEnergy{} + err := json.Unmarshal(raw, res) + return res, err + case "array_energy_reset": + var res = &ArrayEnergyReset{} + err := json.Unmarshal(raw, res) + return res, err + case "vision_turn_signals_command": var res = &VisionTurnSignalsCommand{} err := json.Unmarshal(raw, res) return res, err - case 0x2B1: + case "vision_brake_lights_command": var res = &VisionBrakeLightsCommand{} err := json.Unmarshal(raw, res) return res, err - case 0x2B2: + case "vision_headlights_command": var res = &VisionHeadlightsCommand{} err := json.Unmarshal(raw, res) return res, err - case 0x2B3: + case "vision_horn_command": var res = &VisionHornCommand{} err := json.Unmarshal(raw, res) return res, err - case 0x2B4: + case "vision_array_latches_command": var res = &VisionArrayLatchesCommand{} err := json.Unmarshal(raw, res) return res, err - case 0x2B5: + case "vision_rearview_command": var res = &VisionRearviewCommand{} err := json.Unmarshal(raw, res) return res, err - case 0x610, 0x611, 0x612, 0x613, 0x614, 0x615: + case "tracker_enable": var res = &TrackerEnable{} err := json.Unmarshal(raw, res) - res.Idx = id - 0x610 return res, err - case 0x200: + case "distance_traveled": var res = &DistanceTraveled{} err := json.Unmarshal(raw, res) return res, err - case 0x573: + case "charger_state": var res = &ChargerState{} err := json.Unmarshal(raw, res) return res, err - case 0x74: + case "charger_bms_request": var res = &ChargerBmsRequest{} err := json.Unmarshal(raw, res) return res, err - case 0x576: + case "charger_current_voltage": var res = &ChargerCurrentVoltage{} err := json.Unmarshal(raw, res) return res, err - case 0x577: + case "charger_power": var res = &ChargerPower{} err := json.Unmarshal(raw, res) return res, err - case 0x18E54024: + case "thunderstruck_control_message": var res = &ThunderstruckControlMessage{} err := json.Unmarshal(raw, res) return res, err - case 0x2B6: + case "vision_status_front": var res = &VisionStatusFront{} err := json.Unmarshal(raw, res) return res, err - case 0x2B7: + case "vision_status_rear": var res = &VisionStatusRear{} err := json.Unmarshal(raw, res) return res, err - case 0x300: + case "lights_front_id": var res = &LightsFrontId{} err := json.Unmarshal(raw, res) return res, err - case 0x301: + case "lights_back_id": var res = &LightsBackId{} err := json.Unmarshal(raw, res) return res, err - case 0x302: + case "vision_id": var res = &VisionId{} err := json.Unmarshal(raw, res) return res, err - case 0x240: + case "steering_press_count_1": var res = &SteeringPressCount1{} err := json.Unmarshal(raw, res) return res, err - case 0x250: + case "steering_press_count_2": var res = &SteeringPressCount2{} err := json.Unmarshal(raw, res) return res, err - case 0x241: + case "steering_button_colors_1": var res = &SteeringButtonColors1{} err := json.Unmarshal(raw, res) return res, err - case 0x251: + case "steering_button_colors_2": var res = &SteeringButtonColors2{} err := json.Unmarshal(raw, res) return res, err - case 0x242: + case "steering_horn": var res = &SteeringHorn{} err := json.Unmarshal(raw, res) return res, err - case 0x18EB2440: + case "thunderstruck_status_message": var res = &ThunderstruckStatusMessage{} err := json.Unmarshal(raw, res) return res, err - case 0x600, 0x601, 0x602, 0x603, 0x604, 0x605: + case "tracker_data": var res = &TrackerData{} err := json.Unmarshal(raw, res) - res.Idx = id - 0x600 return res, err - case 0x121: - var res = &TritiumMotorDrive{} + case "tritium_motor_drive_l": + var res = &TritiumMotorDriveL{} err := json.Unmarshal(raw, res) return res, err - case 0x122: - var res = &TritiumMotorPower{} + case "tritium_motor_power_l": + var res = &TritiumMotorPowerL{} err := json.Unmarshal(raw, res) return res, err - case 0x123: - var res = &TritiumReset{} + case "tritium_reset_l": + var res = &TritiumResetL{} err := json.Unmarshal(raw, res) return res, err - case 0x16: + case "tritium_motor_drive_r": + var res = &TritiumMotorDriveR{} + err := json.Unmarshal(raw, res) + return res, err + case "tritium_motor_power_r": + var res = &TritiumMotorPowerR{} + err := json.Unmarshal(raw, res) + return res, err + case "tritium_reset_r": + var res = &TritiumResetR{} + err := json.Unmarshal(raw, res) + return res, err + case "bms_ah_set": var res = &BmsAhSet{} err := json.Unmarshal(raw, res) return res, err - case 0x17: + case "bms_wh_set": var res = &BmsWhSet{} err := json.Unmarshal(raw, res) return res, err - case 0x1A: + case "bms_kill": var res = &BmsKill{} err := json.Unmarshal(raw, res) return res, err - case 0x700: + case "telemetry_rtc_reset": var res = &TelemetryRtcReset{} err := json.Unmarshal(raw, res) return res, err - case 0x140: + case "wsr_identification": var res = &WsrIdentification{} err := json.Unmarshal(raw, res) return res, err - case 0x141: + case "wsr_status_information": var res = &WsrStatusInformation{} err := json.Unmarshal(raw, res) return res, err - case 0x142: + case "wsr_bus_measurement": var res = &WsrBusMeasurement{} err := json.Unmarshal(raw, res) return res, err - case 0x143: + case "wsr_velocity": var res = &WsrVelocity{} err := json.Unmarshal(raw, res) return res, err - case 0x144: + case "wsr_phase_current": var res = &WsrPhaseCurrent{} err := json.Unmarshal(raw, res) return res, err - case 0x145: + case "wsr_motor_voltage_vector": var res = &WsrMotorVoltageVector{} err := json.Unmarshal(raw, res) return res, err - case 0x146: + case "wsr_motor_current_vector": var res = &WsrMotorCurrentVector{} err := json.Unmarshal(raw, res) return res, err - case 0x147: + case "wsr_motor_backemf": var res = &WsrMotorBackemf{} err := json.Unmarshal(raw, res) return res, err - case 0x148: + case "wsr_15_165_voltage_rail": var res = &Wsr15165VoltageRail{} err := json.Unmarshal(raw, res) return res, err - case 0x149: + case "wsr_25_12_voltage_rail": var res = &Wsr2512VoltageRail{} err := json.Unmarshal(raw, res) return res, err - case 0x14B: + case "wsr_heatsink_motor_temp": var res = &WsrHeatsinkMotorTemp{} err := json.Unmarshal(raw, res) return res, err - case 0x14C: + case "wsr_dsp_board_temp": var res = &WsrDspBoardTemp{} err := json.Unmarshal(raw, res) return res, err - case 0x14D: + case "wsr_reserved": var res = &WsrReserved{} err := json.Unmarshal(raw, res) return res, err - case 0x14E: + case "wsr_odometer_bus_amphours_measurement": var res = &WsrOdometerBusAmphoursMeasurement{} err := json.Unmarshal(raw, res) return res, err - case 0x157: + case "wsr_slip_speed_measurement": var res = &WsrSlipSpeedMeasurement{} err := json.Unmarshal(raw, res) return res, err - case 0x100: + case "wsl_identification": var res = &WslIdentification{} err := json.Unmarshal(raw, res) return res, err - case 0x101: + case "wsl_status_information": var res = &WslStatusInformation{} err := json.Unmarshal(raw, res) return res, err - case 0x102: + case "wsl_bus_measurement": var res = &WslBusMeasurement{} err := json.Unmarshal(raw, res) return res, err - case 0x103: + case "wsl_velocity": var res = &WslVelocity{} err := json.Unmarshal(raw, res) return res, err - case 0x104: + case "wsl_phase_current": var res = &WslPhaseCurrent{} err := json.Unmarshal(raw, res) return res, err - case 0x105: + case "wsl_motor_voltage_vector": var res = &WslMotorVoltageVector{} err := json.Unmarshal(raw, res) return res, err - case 0x106: + case "wsl_motor_current_vector": var res = &WslMotorCurrentVector{} err := json.Unmarshal(raw, res) return res, err - case 0x107: + case "wsl_motor_backemf": var res = &WslMotorBackemf{} err := json.Unmarshal(raw, res) return res, err - case 0x108: + case "wsl_15_165_voltage_rail": var res = &Wsl15165VoltageRail{} err := json.Unmarshal(raw, res) return res, err - case 0x109: + case "wsl_25_12_voltage_rail": var res = &Wsl2512VoltageRail{} err := json.Unmarshal(raw, res) return res, err - case 0x10B: + case "wsl_heatsink_motor_temp": var res = &WslHeatsinkMotorTemp{} err := json.Unmarshal(raw, res) return res, err - case 0x10C: + case "wsl_dsp_board_temp": var res = &WslDspBoardTemp{} err := json.Unmarshal(raw, res) return res, err - case 0x10E: + case "wsl_odometer_bus_amphours_measurement": var res = &WslOdometerBusAmphoursMeasurement{} err := json.Unmarshal(raw, res) return res, err - case 0x10D: + case "wsl_reserved": var res = &WslReserved{} err := json.Unmarshal(raw, res) return res, err - case 0x117: + case "wsl_slip_speed_measurement": var res = &WslSlipSpeedMeasurement{} err := json.Unmarshal(raw, res) return res, err } - panic("This should never happen. CAN ID didn't match but was in ID map") + return nil, errors.New("unknown packet name") + } @@ -1155,6 +1227,27 @@ func (p *BatteryStatusLvControlStatus) UnmarshalByte(b byte) { p.StartButton = (b & (1 << 7)) != 0 } +type BatteryStatusPackChoice struct { + LargePack bool `json:"large_pack"` + SmallPack bool `json:"small_pack"` +} + +func (p *BatteryStatusPackChoice) MarshalByte() byte { + var b byte + if p.LargePack { + b |= 1 << 0 + } + if p.SmallPack { + b |= 1 << 1 + } + return b +} + +func (p *BatteryStatusPackChoice) UnmarshalByte(b byte) { + p.LargePack = (b & (1 << 0)) != 0 + p.SmallPack = (b & (1 << 1)) != 0 +} + // BatteryStatus is Status bits for the battery type BatteryStatus struct { @@ -1162,6 +1255,7 @@ type BatteryStatus struct { ContactorState BatteryStatusContactorState `json:"contactor_state"` LvChannelStatus BatteryStatusLvChannelStatus `json:"lv_channel_status"` LvControlStatus BatteryStatusLvControlStatus `json:"lv_control_status"` + PackChoice BatteryStatusPackChoice `json:"pack_choice"` } func (p *BatteryStatus) CANId() (uint32, error) { @@ -1169,15 +1263,16 @@ func (p *BatteryStatus) CANId() (uint32, error) { } func (p *BatteryStatus) Size() uint { - return 4 + return 5 } func (p *BatteryStatus) MarshalPacket() ([]byte, error) { - b := make([]byte, 4) + b := make([]byte, 5) b[0] = p.BatteryState.MarshalByte() b[1] = p.ContactorState.MarshalByte() b[2] = p.LvChannelStatus.MarshalByte() b[3] = p.LvControlStatus.MarshalByte() + b[4] = p.PackChoice.MarshalByte() return b, nil } @@ -1187,6 +1282,7 @@ func (p *BatteryStatus) UnmarshalPacket(b []byte) error { p.ContactorState.UnmarshalByte(b[1]) p.LvChannelStatus.UnmarshalByte(b[2]) p.LvControlStatus.UnmarshalByte(b[3]) + p.PackChoice.UnmarshalByte(b[4]) return nil } @@ -1239,6 +1335,7 @@ type BmsKillReasonReason2 struct { OVERCURRENT bool `json:"OVERCURRENT"` PRECHARGEFAIL bool `json:"PRECHARGE_FAIL"` AUXOVERUNDER bool `json:"AUX_OVER_UNDER"` + AUXOVERTEMP bool `json:"AUX_OVERTEMP"` } func (p *BmsKillReasonReason2) MarshalByte() byte { @@ -1261,6 +1358,9 @@ func (p *BmsKillReasonReason2) MarshalByte() byte { if p.AUXOVERUNDER { b |= 1 << 5 } + if p.AUXOVERTEMP { + b |= 1 << 6 + } return b } @@ -1271,6 +1371,7 @@ func (p *BmsKillReasonReason2) UnmarshalByte(b byte) { p.OVERCURRENT = (b & (1 << 3)) != 0 p.PRECHARGEFAIL = (b & (1 << 4)) != 0 p.AUXOVERUNDER = (b & (1 << 5)) != 0 + p.AUXOVERTEMP = (b & (1 << 6)) != 0 } @@ -1558,9 +1659,9 @@ type BmsModule struct { func (p *BmsModule) CANId() (uint32, error) { if p.Idx >= 36 { - return 0, &UnknownIdError{ 0x1C } + return 0, &UnknownIdError{ 0x40 } } - return 0x1C + p.Idx, nil + return 0x40 + p.Idx, nil } func (p *BmsModule) Size() uint { @@ -1635,6 +1736,302 @@ func (p *BmsChargerResponse) String() string { } +type ChassisIsolationFaultFaultDetected struct { + IsolationFault bool `json:"isolation_fault"` +} + +func (p *ChassisIsolationFaultFaultDetected) MarshalByte() byte { + var b byte + if p.IsolationFault { + b |= 1 << 0 + } + return b +} + +func (p *ChassisIsolationFaultFaultDetected) UnmarshalByte(b byte) { + p.IsolationFault = (b & (1 << 0)) != 0 +} + + +// ChassisIsolationFault is chassiss is not isolated from the battery +type ChassisIsolationFault struct { + FaultDetected ChassisIsolationFaultFaultDetected `json:"fault_detected"` +} + +func (p *ChassisIsolationFault) CANId() (uint32, error) { + return 0x38, nil +} + +func (p *ChassisIsolationFault) Size() uint { + return 1 +} + +func (p *ChassisIsolationFault) MarshalPacket() ([]byte, error) { + b := make([]byte, 1) + b[0] = p.FaultDetected.MarshalByte() + + return b, nil +} + +func (p *ChassisIsolationFault) UnmarshalPacket(b []byte) error { + p.FaultDetected.UnmarshalByte(b[0]) + + return nil +} + +func (p *ChassisIsolationFault) String() string { + return "chassis_isolation_fault" +} + + +type BmsImdInfoDImcStatus1 struct { + IsolationFault bool `json:"isolation_fault"` + ChassisFault bool `json:"chassis_fault"` + SystemFailure bool `json:"system_failure"` + CalibrationRunning bool `json:"calibration_running"` + SelfTestRunning bool `json:"self_test_running"` + IsolationWarning bool `json:"isolation_warning"` + Reserved bool `json:"reserved"` + Reserved2 bool `json:"reserved_2"` +} + +func (p *BmsImdInfoDImcStatus1) MarshalByte() byte { + var b byte + if p.IsolationFault { + b |= 1 << 0 + } + if p.ChassisFault { + b |= 1 << 1 + } + if p.SystemFailure { + b |= 1 << 2 + } + if p.CalibrationRunning { + b |= 1 << 3 + } + if p.SelfTestRunning { + b |= 1 << 4 + } + if p.IsolationWarning { + b |= 1 << 5 + } + if p.Reserved { + b |= 1 << 6 + } + if p.Reserved2 { + b |= 1 << 7 + } + return b +} + +func (p *BmsImdInfoDImcStatus1) UnmarshalByte(b byte) { + p.IsolationFault = (b & (1 << 0)) != 0 + p.ChassisFault = (b & (1 << 1)) != 0 + p.SystemFailure = (b & (1 << 2)) != 0 + p.CalibrationRunning = (b & (1 << 3)) != 0 + p.SelfTestRunning = (b & (1 << 4)) != 0 + p.IsolationWarning = (b & (1 << 5)) != 0 + p.Reserved = (b & (1 << 6)) != 0 + p.Reserved2 = (b & (1 << 7)) != 0 +} + +type BmsImdInfoDImcStatus2 struct { + Reserved bool `json:"reserved"` + Reserved2 bool `json:"reserved_2"` + Reserved3 bool `json:"reserved_3"` + Reserved4 bool `json:"reserved_4"` + Reserved5 bool `json:"reserved_5"` + Reserved6 bool `json:"reserved_6"` + Reserved7 bool `json:"reserved_7"` + Reserved8 bool `json:"reserved_8"` +} + +func (p *BmsImdInfoDImcStatus2) MarshalByte() byte { + var b byte + if p.Reserved { + b |= 1 << 0 + } + if p.Reserved2 { + b |= 1 << 1 + } + if p.Reserved3 { + b |= 1 << 2 + } + if p.Reserved4 { + b |= 1 << 3 + } + if p.Reserved5 { + b |= 1 << 4 + } + if p.Reserved6 { + b |= 1 << 5 + } + if p.Reserved7 { + b |= 1 << 6 + } + if p.Reserved8 { + b |= 1 << 7 + } + return b +} + +func (p *BmsImdInfoDImcStatus2) UnmarshalByte(b byte) { + p.Reserved = (b & (1 << 0)) != 0 + p.Reserved2 = (b & (1 << 1)) != 0 + p.Reserved3 = (b & (1 << 2)) != 0 + p.Reserved4 = (b & (1 << 3)) != 0 + p.Reserved5 = (b & (1 << 4)) != 0 + p.Reserved6 = (b & (1 << 5)) != 0 + p.Reserved7 = (b & (1 << 6)) != 0 + p.Reserved8 = (b & (1 << 7)) != 0 +} + +type BmsImdInfoDVifcStatus1 struct { + InsulationMeasurment bool `json:"insulation_measurment"` + ImcConnectivityNotImplemented bool `json:"imc_connectivity_not_implemented"` + ImcAliveSatusDetection bool `json:"imc_alive_satus_detection"` + Reserved bool `json:"reserved"` + VifcCommandNotImplemented bool `json:"vifc_command_not_implemented"` + Reserved2 bool `json:"reserved_2"` + Reserved3 bool `json:"reserved_3"` + Reserved4 bool `json:"reserved_4"` +} + +func (p *BmsImdInfoDVifcStatus1) MarshalByte() byte { + var b byte + if p.InsulationMeasurment { + b |= 1 << 0 + } + if p.ImcConnectivityNotImplemented { + b |= 1 << 1 + } + if p.ImcAliveSatusDetection { + b |= 1 << 2 + } + if p.Reserved { + b |= 1 << 3 + } + if p.VifcCommandNotImplemented { + b |= 1 << 4 + } + if p.Reserved2 { + b |= 1 << 5 + } + if p.Reserved3 { + b |= 1 << 6 + } + if p.Reserved4 { + b |= 1 << 7 + } + return b +} + +func (p *BmsImdInfoDVifcStatus1) UnmarshalByte(b byte) { + p.InsulationMeasurment = (b & (1 << 0)) != 0 + p.ImcConnectivityNotImplemented = (b & (1 << 1)) != 0 + p.ImcAliveSatusDetection = (b & (1 << 2)) != 0 + p.Reserved = (b & (1 << 3)) != 0 + p.VifcCommandNotImplemented = (b & (1 << 4)) != 0 + p.Reserved2 = (b & (1 << 5)) != 0 + p.Reserved3 = (b & (1 << 6)) != 0 + p.Reserved4 = (b & (1 << 7)) != 0 +} + +type BmsImdInfoDVifcStatus2 struct { + InsulationResistanceValue bool `json:"insulation_resistance_value"` + Reserved bool `json:"reserved"` + Reserved2 bool `json:"reserved_2"` + Reserved3 bool `json:"reserved_3"` + ImcSelfTestOverAll bool `json:"imc_self_test_overAll"` + ImcSelfTestParameterConfig bool `json:"imc_self_test_parameterConfig"` + Reserved4 bool `json:"reserved_4"` + Reserved5 bool `json:"reserved_5"` +} + +func (p *BmsImdInfoDVifcStatus2) MarshalByte() byte { + var b byte + if p.InsulationResistanceValue { + b |= 1 << 0 + } + if p.Reserved { + b |= 1 << 1 + } + if p.Reserved2 { + b |= 1 << 2 + } + if p.Reserved3 { + b |= 1 << 3 + } + if p.ImcSelfTestOverAll { + b |= 1 << 4 + } + if p.ImcSelfTestParameterConfig { + b |= 1 << 5 + } + if p.Reserved4 { + b |= 1 << 6 + } + if p.Reserved5 { + b |= 1 << 7 + } + return b +} + +func (p *BmsImdInfoDVifcStatus2) UnmarshalByte(b byte) { + p.InsulationResistanceValue = (b & (1 << 0)) != 0 + p.Reserved = (b & (1 << 1)) != 0 + p.Reserved2 = (b & (1 << 2)) != 0 + p.Reserved3 = (b & (1 << 3)) != 0 + p.ImcSelfTestOverAll = (b & (1 << 4)) != 0 + p.ImcSelfTestParameterConfig = (b & (1 << 5)) != 0 + p.Reserved4 = (b & (1 << 6)) != 0 + p.Reserved5 = (b & (1 << 7)) != 0 +} + + +// BmsImdInfo is information from chassis isolation +type BmsImdInfo struct { + DImcRIso uint16 `json:"d_imc_r_iso"` + DImcStatus1 BmsImdInfoDImcStatus1 `json:"d_imc_status_1"` + DImcStatus2 BmsImdInfoDImcStatus2 `json:"d_imc_status_2"` + DVifcStatus1 BmsImdInfoDVifcStatus1 `json:"d_vifc_status_1"` + DVifcStatus2 BmsImdInfoDVifcStatus2 `json:"d_vifc_status_2"` +} + +func (p *BmsImdInfo) CANId() (uint32, error) { + return 0x37, nil +} + +func (p *BmsImdInfo) Size() uint { + return 6 +} + +func (p *BmsImdInfo) MarshalPacket() ([]byte, error) { + b := make([]byte, 6) + binary.LittleEndian.PutUint16(b[0:], p.DImcRIso) + b[2] = p.DImcStatus1.MarshalByte() + b[3] = p.DImcStatus2.MarshalByte() + b[4] = p.DVifcStatus1.MarshalByte() + b[5] = p.DVifcStatus2.MarshalByte() + + return b, nil +} + +func (p *BmsImdInfo) UnmarshalPacket(b []byte) error { + p.DImcRIso = binary.LittleEndian.Uint16(b[0:]) + p.DImcStatus1.UnmarshalByte(b[2]) + p.DImcStatus2.UnmarshalByte(b[3]) + p.DVifcStatus1.UnmarshalByte(b[4]) + p.DVifcStatus2.UnmarshalByte(b[5]) + + return nil +} + +func (p *BmsImdInfo) String() string { + return "bms_imd_info" +} + + // DashboardPedalPercentages is ADC values from the brake and accelerator pedals. type DashboardPedalPercentages struct { @@ -2169,6 +2566,72 @@ func (p *ArrayPower) String() string { } + +// ArrayEnergy is cumulative energy received from the array +type ArrayEnergy struct { + // 0 Joule + Energy float32 `json:"energy"` +} + +func (p *ArrayEnergy) CANId() (uint32, error) { + return 0x119, nil +} + +func (p *ArrayEnergy) Size() uint { + return 4 +} + +func (p *ArrayEnergy) MarshalPacket() ([]byte, error) { + b := make([]byte, 4) + float32ToBytes(b[0:], p.Energy, false) + + return b, nil +} + +func (p *ArrayEnergy) UnmarshalPacket(b []byte) error { + p.Energy = float32FromBytes(b[0:], false) + + return nil +} + +func (p *ArrayEnergy) String() string { + return "array_energy" +} + + + +// ArrayEnergyReset is resets cumulative energy received from the array +type ArrayEnergyReset struct { + // 0 Joule + Energy float32 `json:"energy"` +} + +func (p *ArrayEnergyReset) CANId() (uint32, error) { + return 0x120, nil +} + +func (p *ArrayEnergyReset) Size() uint { + return 4 +} + +func (p *ArrayEnergyReset) MarshalPacket() ([]byte, error) { + b := make([]byte, 4) + float32ToBytes(b[0:], p.Energy, false) + + return b, nil +} + +func (p *ArrayEnergyReset) UnmarshalPacket(b []byte) error { + p.Energy = float32FromBytes(b[0:], false) + + return nil +} + +func (p *ArrayEnergyReset) String() string { + return "array_energy_reset" +} + + type VisionTurnSignalsCommandLights struct { LeftTurnSignal bool `json:"left_turn_signal"` RightTurnSignal bool `json:"right_turn_signal"` @@ -2580,7 +3043,7 @@ type DistanceTraveled struct { } func (p *DistanceTraveled) CANId() (uint32, error) { - return 0x200, nil + return 0x19D, nil } func (p *DistanceTraveled) Size() uint { @@ -3669,21 +4132,21 @@ func (p *TrackerData) String() string { -// TritiumMotorDrive is Tritium Motor Drive Command -type TritiumMotorDrive struct { +// TritiumMotorDriveL is Tritium Motor Drive Command +type TritiumMotorDriveL struct { MotorVelocity float32 `json:"motor_velocity"` MotorCurrent float32 `json:"motor_current"` } -func (p *TritiumMotorDrive) CANId() (uint32, error) { +func (p *TritiumMotorDriveL) CANId() (uint32, error) { return 0x121, nil } -func (p *TritiumMotorDrive) Size() uint { +func (p *TritiumMotorDriveL) Size() uint { return 8 } -func (p *TritiumMotorDrive) MarshalPacket() ([]byte, error) { +func (p *TritiumMotorDriveL) MarshalPacket() ([]byte, error) { b := make([]byte, 8) float32ToBytes(b[0:], p.MotorVelocity, false) float32ToBytes(b[4:], p.MotorCurrent, false) @@ -3691,34 +4154,34 @@ func (p *TritiumMotorDrive) MarshalPacket() ([]byte, error) { return b, nil } -func (p *TritiumMotorDrive) UnmarshalPacket(b []byte) error { +func (p *TritiumMotorDriveL) UnmarshalPacket(b []byte) error { p.MotorVelocity = float32FromBytes(b[0:], false) p.MotorCurrent = float32FromBytes(b[4:], false) return nil } -func (p *TritiumMotorDrive) String() string { - return "tritium_motor_drive" +func (p *TritiumMotorDriveL) String() string { + return "tritium_motor_drive_l" } -// TritiumMotorPower is Tritium Motor Power Command -type TritiumMotorPower struct { +// TritiumMotorPowerL is Tritium Motor Power Command +type TritiumMotorPowerL struct { Reserved float32 `json:"reserved"` BusCurrent float32 `json:"bus_current"` } -func (p *TritiumMotorPower) CANId() (uint32, error) { +func (p *TritiumMotorPowerL) CANId() (uint32, error) { return 0x122, nil } -func (p *TritiumMotorPower) Size() uint { +func (p *TritiumMotorPowerL) Size() uint { return 8 } -func (p *TritiumMotorPower) MarshalPacket() ([]byte, error) { +func (p *TritiumMotorPowerL) MarshalPacket() ([]byte, error) { b := make([]byte, 8) float32ToBytes(b[0:], p.Reserved, false) float32ToBytes(b[4:], p.BusCurrent, false) @@ -3726,34 +4189,34 @@ func (p *TritiumMotorPower) MarshalPacket() ([]byte, error) { return b, nil } -func (p *TritiumMotorPower) UnmarshalPacket(b []byte) error { +func (p *TritiumMotorPowerL) UnmarshalPacket(b []byte) error { p.Reserved = float32FromBytes(b[0:], false) p.BusCurrent = float32FromBytes(b[4:], false) return nil } -func (p *TritiumMotorPower) String() string { - return "tritium_motor_power" +func (p *TritiumMotorPowerL) String() string { + return "tritium_motor_power_l" } -// TritiumReset is Tritium Reset Command -type TritiumReset struct { +// TritiumResetL is Tritium Reset Command +type TritiumResetL struct { Unused1 float32 `json:"unused1"` Unused2 float32 `json:"unused2"` } -func (p *TritiumReset) CANId() (uint32, error) { +func (p *TritiumResetL) CANId() (uint32, error) { return 0x123, nil } -func (p *TritiumReset) Size() uint { +func (p *TritiumResetL) Size() uint { return 8 } -func (p *TritiumReset) MarshalPacket() ([]byte, error) { +func (p *TritiumResetL) MarshalPacket() ([]byte, error) { b := make([]byte, 8) float32ToBytes(b[0:], p.Unused1, false) float32ToBytes(b[4:], p.Unused2, false) @@ -3761,15 +4224,120 @@ func (p *TritiumReset) MarshalPacket() ([]byte, error) { return b, nil } -func (p *TritiumReset) UnmarshalPacket(b []byte) error { +func (p *TritiumResetL) UnmarshalPacket(b []byte) error { p.Unused1 = float32FromBytes(b[0:], false) p.Unused2 = float32FromBytes(b[4:], false) return nil } -func (p *TritiumReset) String() string { - return "tritium_reset" +func (p *TritiumResetL) String() string { + return "tritium_reset_l" +} + + + +// TritiumMotorDriveR is Tritium Motor Drive Command +type TritiumMotorDriveR struct { + MotorVelocity float32 `json:"motor_velocity"` + MotorCurrent float32 `json:"motor_current"` +} + +func (p *TritiumMotorDriveR) CANId() (uint32, error) { + return 0x161, nil +} + +func (p *TritiumMotorDriveR) Size() uint { + return 8 +} + +func (p *TritiumMotorDriveR) MarshalPacket() ([]byte, error) { + b := make([]byte, 8) + float32ToBytes(b[0:], p.MotorVelocity, false) + float32ToBytes(b[4:], p.MotorCurrent, false) + + return b, nil +} + +func (p *TritiumMotorDriveR) UnmarshalPacket(b []byte) error { + p.MotorVelocity = float32FromBytes(b[0:], false) + p.MotorCurrent = float32FromBytes(b[4:], false) + + return nil +} + +func (p *TritiumMotorDriveR) String() string { + return "tritium_motor_drive_r" +} + + + +// TritiumMotorPowerR is Tritium Motor Power Command +type TritiumMotorPowerR struct { + Reserved float32 `json:"reserved"` + BusCurrent float32 `json:"bus_current"` +} + +func (p *TritiumMotorPowerR) CANId() (uint32, error) { + return 0x162, nil +} + +func (p *TritiumMotorPowerR) Size() uint { + return 8 +} + +func (p *TritiumMotorPowerR) MarshalPacket() ([]byte, error) { + b := make([]byte, 8) + float32ToBytes(b[0:], p.Reserved, false) + float32ToBytes(b[4:], p.BusCurrent, false) + + return b, nil +} + +func (p *TritiumMotorPowerR) UnmarshalPacket(b []byte) error { + p.Reserved = float32FromBytes(b[0:], false) + p.BusCurrent = float32FromBytes(b[4:], false) + + return nil +} + +func (p *TritiumMotorPowerR) String() string { + return "tritium_motor_power_r" +} + + + +// TritiumResetR is Tritium Reset Command +type TritiumResetR struct { + Unused1 float32 `json:"unused1"` + Unused2 float32 `json:"unused2"` +} + +func (p *TritiumResetR) CANId() (uint32, error) { + return 0x163, nil +} + +func (p *TritiumResetR) Size() uint { + return 8 +} + +func (p *TritiumResetR) MarshalPacket() ([]byte, error) { + b := make([]byte, 8) + float32ToBytes(b[0:], p.Unused1, false) + float32ToBytes(b[4:], p.Unused2, false) + + return b, nil +} + +func (p *TritiumResetR) UnmarshalPacket(b []byte) error { + p.Unused1 = float32FromBytes(b[0:], false) + p.Unused2 = float32FromBytes(b[4:], false) + + return nil +} + +func (p *TritiumResetR) String() string { + return "tritium_reset_r" } @@ -5363,4 +5931,4 @@ func (p *WslSlipSpeedMeasurement) String() string { // The json representation that was used to generate this data. // can be used to share the parsing data for i.e dynamic python gui. -const SkylabDefinitions = `{"packets":[{"name":"bms_measurement","description":"Voltages for main battery and aux pack","id":16,"endian":"little","repeat":0,"offset":0,"data":[{"name":"battery_voltage","type":"uint16_t","units":"V","conversion":0.01,"bits":null},{"name":"aux_voltage","type":"uint16_t","units":"V","conversion":0.001,"bits":null},{"name":"current","type":"float","units":"A","conversion":1,"bits":null}]},{"name":"battery_status","description":"Status bits for the battery","id":17,"endian":"little","repeat":0,"offset":0,"data":[{"name":"battery_state","type":"bitfield","units":"","conversion":0,"bits":[{"name":"startup"},{"name":"precharge"},{"name":"discharging"},{"name":"lv_only"},{"name":"charging"},{"name":"wall_charging"},{"name":"killed"}]},{"name":"contactor_state","type":"bitfield","units":"","conversion":0,"bits":[{"name":"battery_high_contactor"},{"name":"battery_low_contactor"},{"name":"battery_vicor_contactor"},{"name":"battery_pre_contactor"},{"name":"battery_high2_contactor"},{"name":"battery_low2_contactor"},{"name":"charger_high_contactor"},{"name":"charger_pre_contactor"}]},{"name":"lv_channel_status","type":"bitfield","units":"","conversion":0,"bits":[{"name":"aux_fault"},{"name":"main_fault"},{"name":"aux_power_valid"},{"name":"main_power_valid"},{"name":"aux_power_active"},{"name":"main_power_active"}]},{"name":"lv_control_status","type":"bitfield","units":"","conversion":0,"bits":[{"name":"aux_vicor_enable"},{"name":"bat_vicor_enable"},{"name":"aux_relay_held"},{"name":"aux_ref_enable"},{"name":"aux_charging_enable"},{"name":"kill_hv"},{"name":"kill_lv"},{"name":"start_button"}]}]},{"name":"bms_kill_reason","description":"Information for when the car kills","id":18,"endian":"little","repeat":0,"offset":0,"data":[{"name":"reason1","type":"bitfield","units":"","conversion":0,"bits":[{"name":"OVERVOLT"},{"name":"UNDERVOLT"},{"name":"OVERTEMP"},{"name":"TEMP_DISCONNECT"},{"name":"COMM_FAIL"}]},{"name":"reason2","type":"bitfield","units":"","conversion":0,"bits":[{"name":"HARDWARE"},{"name":"KILL_PACKET"},{"name":"UKNOWN"},{"name":"OVERCURRENT"},{"name":"PRECHARGE_FAIL"},{"name":"AUX_OVER_UNDER"}]},{"name":"module","type":"uint16_t","units":"","conversion":0,"bits":null},{"name":"value","type":"float","units":"","conversion":0,"bits":null}]},{"name":"bms_module_min_max","description":"min and max cell voltages and temperatures","id":19,"endian":"little","repeat":0,"offset":0,"data":[{"name":"module_max_temp","type":"int16_t","units":"C","conversion":0.01,"bits":null},{"name":"module_min_temp","type":"int16_t","units":"C","conversion":0.01,"bits":null},{"name":"module_max_voltage","type":"uint16_t","units":"V","conversion":0.001,"bits":null},{"name":"module_min_voltage","type":"uint16_t","units":"V","conversion":0.001,"bits":null}]},{"name":"bms_soc","description":"State of charge","id":20,"endian":"little","repeat":0,"offset":0,"data":[{"name":"soc","type":"float","units":"","conversion":1,"bits":null}]},{"name":"bms_capacity","description":"State of charge","id":21,"endian":"little","repeat":0,"offset":0,"data":[{"name":"Ah","type":"float","units":"","conversion":1,"bits":null},{"name":"Wh","type":"float","units":"","conversion":1,"bits":null}]},{"name":"bms_currentlimit","description":"reports BP params for current","id":24,"endian":"little","repeat":0,"offset":0,"data":[{"name":"current_max","type":"int16_t","units":"A","conversion":0.01,"bits":null},{"name":"current_min","type":"int16_t","units":"A","conversion":0.01,"bits":null}]},{"name":"bms_fan_info","description":"BP Fans","id":25,"endian":"little","repeat":0,"offset":0,"data":[{"name":"fan1","type":"uint16_t","units":"RPM","conversion":1,"bits":null},{"name":"fan2","type":"uint16_t","units":"RPM","conversion":1,"bits":null},{"name":"fan3","type":"uint16_t","units":"RPM","conversion":1,"bits":null},{"name":"fan4","type":"uint16_t","units":"RPM","conversion":1,"bits":null}]},{"name":"bms_set_min_fan_speed","description":"packet which sets a minimum fan speed of BMS for a specific time frame in seconds","id":27,"endian":"little","repeat":0,"offset":0,"data":[{"name":"fan_percentage","type":"float","units":"percent","conversion":0,"bits":null},{"name":"time","type":"uint16_t","units":"s","conversion":0,"bits":null}]},{"name":"bms_module","description":"Voltage and temperature for a single module","id":28,"endian":"little","repeat":36,"offset":1,"data":[{"name":"voltage","type":"float","units":"V","conversion":1,"bits":null},{"name":"temperature","type":"float","units":"C","conversion":1,"bits":null}]},{"name":"bms_charger_response","description":"Response packet from BMS for indicating whether BMS is ready for charging","id":117,"endian":"","repeat":0,"offset":0,"data":[{"name":"response_flags","type":"bitfield","units":"","conversion":0,"bits":[{"name":"charging_ready"}]}]},{"name":"dashboard_pedal_percentages","description":"ADC values from the brake and accelerator pedals.","id":656,"endian":"little","repeat":0,"offset":0,"data":[{"name":"accel_pedal_value","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"brake_pedal_value","type":"uint8_t","units":"","conversion":0,"bits":null}]},{"name":"car_state","description":"Car gear. Forward, neutral, reverse, etc.","id":657,"endian":"little","repeat":0,"offset":0,"data":[{"name":"state","type":"uint8_t","units":"","conversion":0,"bits":null}]},{"name":"dashboard_pedal_fault","description":"Target speed that the driver should maintain.","id":658,"endian":"little","repeat":0,"offset":0,"data":[{"name":"brake_fault_count","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"accel_fault_count","type":"uint8_t","units":"","conversion":0,"bits":null}]},{"name":"dashboard_system_timeout_test","description":"Exposes whether each system that dashboard is supposed to listen for packets from has sent a packet. Used for testing.","id":665,"endian":"little","repeat":0,"offset":0,"data":[{"name":"flag_set_0","type":"bitfield","units":"","conversion":0,"bits":[{"name":"steering_disconnected"},{"name":"vision_front_disconnected"},{"name":"vision_rear_disconnected"},{"name":"telemetry_disconnected"},{"name":"wsl_disconnected"},{"name":"wsr_disconnected"},{"name":"front_mppt_disconnected"},{"name":"rear_mppt_disconnected"}]}]},{"name":"car_speed","description":"speed of car in meters per second","id":666,"endian":"little","repeat":0,"offset":0,"data":[{"name":"speed","type":"float","units":"","conversion":0,"bits":null}]},{"name":"flight_computer_lv_board_disconnect_counts","description":"Number of times a board hasn't been heard from within the allowed timeout.","id":667,"endian":"little","repeat":0,"offset":0,"data":[{"name":"front_lights","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"rear_lights","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"steering","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"vision","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"driver_display","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"center_console","type":"uint8_t","units":"","conversion":0,"bits":null}]},{"name":"flight_computer_hv_board_disconnect_counts","description":"Number of times a board hasn't been heard from within the allowed timeout.","id":668,"endian":"little","repeat":0,"offset":0,"data":[{"name":"bms","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"charger","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"wsl","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"wsr","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"mppt_front","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"mppt_rear","type":"uint8_t","units":"","conversion":0,"bits":null}]},{"name":"flight_computer_internal_state","description":"internal bools","id":669,"endian":"little","repeat":0,"offset":0,"data":[{"name":"bms","type":"bitfield","units":"","conversion":0,"bits":[{"name":"battery_kill"},{"name":"cells_in_charging_threshold"},{"name":"first_packet_received"}]},{"name":"charger","type":"bitfield","units":"","conversion":0,"bits":[{"name":"proximity_detected"}]},{"name":"photon3","type":"bitfield","units":"","conversion":0,"bits":[{"name":"enable"}]},{"name":"wavesculptor","type":"bitfield","units":"","conversion":0,"bits":[{"name":"sending_reset"},{"name":"regen_enable"}]},{"name":"internal","type":"bitfield","units":"","conversion":0,"bits":[{"name":"accel_pedal_disconnect"},{"name":"brake_pedal_disconnect"}]}]},{"name":"power_to_drive","description":"calculated power required to drive the vehicle","id":414,"endian":"little","repeat":0,"offset":0,"data":[{"name":"moving_average_100","type":"int16_t","units":"","conversion":0,"bits":null},{"name":"moving_average_1k","type":"int16_t","units":"","conversion":0,"bits":null},{"name":"moving_average_10k","type":"int16_t","units":"","conversion":0,"bits":null}]},{"name":"array_power","description":"array power calculated from current and voltage measurements","id":415,"endian":"little","repeat":0,"offset":0,"data":[{"name":"front_array_channel_0","type":"uint16_t","units":"","conversion":0,"bits":null},{"name":"front_array_channel_1","type":"uint16_t","units":"","conversion":0,"bits":null},{"name":"rear_array_channel_0","type":"uint16_t","units":"","conversion":0,"bits":null},{"name":"rear_array_channel_1","type":"uint16_t","units":"","conversion":0,"bits":null}]},{"name":"vision_turn_signals_command","description":"Command to have the vision board illuminate or turn off left, right, or both turn signals","id":688,"endian":"","repeat":0,"offset":0,"data":[{"name":"lights","type":"bitfield","units":"","conversion":0,"bits":[{"name":"left_turn_signal"},{"name":"right_turn_signal"},{"name":"spare_1"},{"name":"spare_2"},{"name":"spare_3"}]}]},{"name":"vision_brake_lights_command","description":"Command to have the vision board illuminate or turn off the brake lights","id":689,"endian":"","repeat":0,"offset":0,"data":[{"name":"lights","type":"bitfield","units":"","conversion":0,"bits":[{"name":"brake_lights"},{"name":"spare_1"},{"name":"spare_2"},{"name":"spare_3"}]}]},{"name":"vision_headlights_command","description":"Command to have the vision board illuminate or turn off the headlights and high beams","id":690,"endian":"","repeat":0,"offset":0,"data":[{"name":"lights","type":"bitfield","units":"","conversion":0,"bits":[{"name":"headlights"},{"name":"high_beams"},{"name":"spare_1"},{"name":"spare_2"},{"name":"spare_3"}]},{"name":"brightness","type":"float","units":"","conversion":0,"bits":null}]},{"name":"vision_horn_command","description":"Command the vision board honk the horn, must be repeatedly sent otherwise the vision board will stop honking after a bit. See high_power.h for details.","id":691,"endian":"","repeat":0,"offset":0,"data":[{"name":"horn","type":"bitfield","units":"","conversion":0,"bits":[{"name":"horn"},{"name":"spare"}]}]},{"name":"vision_array_latches_command","description":"Command the vision board to open the array latches","id":692,"endian":"","repeat":0,"offset":0,"data":[{"name":"array_latches","type":"bitfield","units":"","conversion":0,"bits":[{"name":"array_front"},{"name":"array_rear"}]}]},{"name":"vision_rearview_command","description":"Command the vision board turn on the rear view cameras","id":693,"endian":"","repeat":0,"offset":0,"data":[{"name":"cameras","type":"bitfield","units":"","conversion":0,"bits":[{"name":"left"},{"name":"right"},{"name":"rear"}]}]},{"name":"tracker_enable","description":"Enables/disables power trackers. Use 0x610 for the channel transmitting the data packet on 0x600, 0x611 for 0x601, et cetera. Sending 1 in the enable byte turns the tracker on; sending 0 turns it off.","id":1552,"endian":"little","repeat":6,"offset":1,"data":[{"name":"enable","type":"uint8_t","units":"","conversion":0,"bits":null}]},{"name":"distance_traveled","description":"distance of wavesculptor odometer","id":512,"endian":"little","repeat":0,"offset":0,"data":[{"name":"trip_distance","type":"float","units":"m","conversion":0,"bits":null}]},{"name":"charger_state","description":"Notifies whether the J1772 cable is plugged.","id":1395,"endian":"","repeat":0,"offset":0,"data":[{"name":"state_flags","type":"bitfield","units":"","conversion":0,"bits":[{"name":"charger_plugged"}]},{"name":"charger_max_temp","type":"uint16_t","units":"C","conversion":0.001,"bits":null},{"name":"fault","type":"bitfield","units":"","conversion":0,"bits":[{"name":"CHARGER_OVERVOLT"},{"name":"CHARGER_OVERTEMP"},{"name":"CHARGER_CAN_TIMEOUT"},{"name":"BATTERY_HV_KILL"},{"name":"BATTERY_UNDERVOLT"},{"name":"BATTERY_OVERVOLT"},{"name":"BATTERY_CELL_OVERTEMP"},{"name":"BATTERY_CAN_TIMEOUT"}]},{"name":"charging_current","type":"float","units":"A","conversion":0,"bits":null}]},{"name":"charger_bms_request","description":"Request packet for sending contactor commands from the charger to BP.","id":116,"endian":"","repeat":0,"offset":0,"data":[{"name":"request_flags","type":"bitfield","units":"","conversion":0,"bits":[{"name":"charging_requested"}]}]},{"name":"charger_current_voltage","description":"Packet to request charging current/voltage set","id":1398,"endian":"","repeat":0,"offset":0,"data":[{"name":"max_current","type":"float","units":"A","conversion":0,"bits":null},{"name":"max_capacity","type":"float","units":"kWh","conversion":0,"bits":null}]},{"name":"charger_power","description":"Outputs the amount of power that the chargers are delivering.","id":1399,"endian":"","repeat":0,"offset":0,"data":[{"name":"power","type":"float","units":"W","conversion":0,"bits":null}]},{"name":"thunderstruck_control_message","description":"Control packet for thunderstruck chargers","id":417677348,"endian":"little","repeat":0,"offset":0,"data":[{"name":"Enable","type":"uint8_t","units":"V","conversion":1,"bits":null},{"name":"CHARGE_VOLTAGE","type":"uint16_t","units":"V","conversion":1,"bits":null},{"name":"CHARGE_CURRENT","type":"uint16_t","units":"V","conversion":1,"bits":null},{"name":"LED_BLINK_PATTERN","type":"uint8_t","units":"V","conversion":1,"bits":null},{"name":"RESERVED","type":"uint16_t","units":"V","conversion":1,"bits":null}]},{"name":"vision_status_front","description":"Status of the front vision board outputs","id":694,"endian":"","repeat":0,"offset":0,"data":[{"name":"lights","type":"bitfield","units":"","conversion":0,"bits":[{"name":"left_turn_signal"},{"name":"right_turn_signal"},{"name":"brake_lights"},{"name":"headlights"},{"name":"high_beams"},{"name":"spare_1"},{"name":"spare_2"},{"name":"spare_3"}]},{"name":"horn","type":"bitfield","units":"","conversion":0,"bits":[{"name":"horn"},{"name":"spare"}]},{"name":"cameras","type":"bitfield","units":"","conversion":0,"bits":[{"name":"left"},{"name":"right"},{"name":"rear"}]},{"name":"array_latches","type":"bitfield","units":"","conversion":0,"bits":[{"name":"array_front_0"},{"name":"array_front_1"},{"name":"array_rear_0"},{"name":"array_rear_1"}]}]},{"name":"vision_status_rear","description":"Status of the rear vision board outputs","id":695,"endian":"","repeat":0,"offset":0,"data":[{"name":"lights","type":"bitfield","units":"","conversion":0,"bits":[{"name":"left_turn_signal"},{"name":"right_turn_signal"},{"name":"brake_lights"},{"name":"headlights"},{"name":"high_beams"},{"name":"spare_1"},{"name":"spare_2"},{"name":"spare_3"}]},{"name":"horn","type":"bitfield","units":"","conversion":0,"bits":[{"name":"horn"},{"name":"spare"}]},{"name":"cameras","type":"bitfield","units":"","conversion":0,"bits":[{"name":"left"},{"name":"right"},{"name":"rear"}]},{"name":"array_latches","type":"bitfield","units":"","conversion":0,"bits":[{"name":"array_front_0"},{"name":"array_front_1"},{"name":"array_rear_0"},{"name":"array_rear_1"}]}]},{"name":"lights_front_id","description":"Unique identification packet for front lights board","id":768,"endian":"","repeat":0,"offset":0,"data":[{"name":"board_id","type":"uint16_t","units":"","conversion":0,"bits":null},{"name":"mcu_temp","type":"int16_t","units":"C","conversion":0.01,"bits":null},{"name":"bus_voltage","type":"uint16_t","units":"V","conversion":0.001,"bits":null},{"name":"fault_code","type":"uint16_t","units":"","conversion":0,"bits":null}]},{"name":"lights_back_id","description":"Unique identification packet for back lights board","id":769,"endian":"","repeat":0,"offset":0,"data":[{"name":"board_id","type":"uint16_t","units":"","conversion":0,"bits":null},{"name":"mcu_temp","type":"int16_t","units":"C","conversion":0.01,"bits":null},{"name":"bus_voltage","type":"uint16_t","units":"V","conversion":0.001,"bits":null},{"name":"fault_code","type":"uint16_t","units":"","conversion":0,"bits":null}]},{"name":"vision_id","description":"Unique identification packet for vision","id":770,"endian":"","repeat":0,"offset":0,"data":[{"name":"board_id","type":"uint16_t","units":"","conversion":0,"bits":null},{"name":"mcu_temp","type":"int16_t","units":"C","conversion":0.01,"bits":null},{"name":"bus_voltage","type":"uint16_t","units":"V","conversion":0.001,"bits":null},{"name":"fault_code","type":"uint16_t","units":"","conversion":0,"bits":null}]},{"name":"steering_press_count_1","description":"Shows whether each button has been toggled an even (\"on\") or odd (\"off\") number of times.","id":576,"endian":"","repeat":0,"offset":0,"data":[{"name":"button0","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button1","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button2","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button3","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button4","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button5","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button6","type":"uint8_t","units":"","conversion":0,"bits":null}]},{"name":"steering_press_count_2","description":"Shows whether each button has been toggled an even (\"on\") or odd (\"off\") number of times.","id":592,"endian":"","repeat":0,"offset":0,"data":[{"name":"button7","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button8","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button9","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button10","type":"uint8_t","units":"","conversion":0,"bits":null}]},{"name":"steering_button_colors_1","description":"This packet controls each button's color. Each byte is a hex color code.","id":577,"endian":"","repeat":0,"offset":0,"data":[{"name":"button0","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button1","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button2","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button3","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button4","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button5","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button6","type":"uint8_t","units":"","conversion":0,"bits":null}]},{"name":"steering_button_colors_2","description":"This packet controls each button's color. Each byte is a hex color code.","id":593,"endian":"","repeat":0,"offset":0,"data":[{"name":"button7","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button8","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button9","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"button10","type":"uint8_t","units":"","conversion":0,"bits":null}]},{"name":"steering_horn","description":"This packet controls the state of the horn.","id":578,"endian":"","repeat":0,"offset":0,"data":[{"name":"horn","type":"uint8_t","units":"","conversion":0,"bits":null}]},{"name":"thunderstruck_status_message","description":"Status packet for thunderstruck chargers","id":418063424,"endian":"little","repeat":0,"offset":0,"data":[{"name":"STATUS_FLAGS","type":"uint8_t","units":"V","conversion":1,"bits":null},{"name":"CHARGE_FLAGS","type":"uint8_t","units":"V","conversion":1,"bits":null},{"name":"OUTPUT_VOLTAGE","type":"uint16_t","units":"V","conversion":1,"bits":null},{"name":"OUTPUT_CURRENT","type":"uint16_t","units":"V","conversion":1,"bits":null},{"name":"CHARGER_TEMP","type":"uint8_t","units":"V","conversion":1,"bits":null},{"name":"RESERVED","type":"uint8_t","units":"V","conversion":1,"bits":null}]},{"name":"tracker_data","description":"Tracker data. Each channel transmits on a specific ID, which should be specified along with the tracker, most likely 0x600-0x603.","id":1536,"endian":"little","repeat":6,"offset":1,"data":[{"name":"array_voltage","type":"uint16_t","units":"V","conversion":0.01,"bits":null},{"name":"array_current","type":"uint16_t","units":"A","conversion":0.001,"bits":null},{"name":"battery_voltage","type":"uint16_t","units":"V","conversion":0.01,"bits":null},{"name":"temperature","type":"uint16_t","units":"C","conversion":0.01,"bits":null}]},{"name":"tritium_motor_drive","description":"Tritium Motor Drive Command","id":289,"endian":"little","repeat":0,"offset":0,"data":[{"name":"motor_velocity","type":"float","units":"","conversion":0,"bits":null},{"name":"motor_current","type":"float","units":"","conversion":0,"bits":null}]},{"name":"tritium_motor_power","description":"Tritium Motor Power Command","id":290,"endian":"little","repeat":0,"offset":0,"data":[{"name":"reserved","type":"float","units":"","conversion":0,"bits":null},{"name":"bus_current","type":"float","units":"","conversion":0,"bits":null}]},{"name":"tritium_reset","description":"Tritium Reset Command","id":291,"endian":"little","repeat":0,"offset":0,"data":[{"name":"unused1","type":"float","units":"","conversion":0,"bits":null},{"name":"unused2","type":"float","units":"","conversion":0,"bits":null}]},{"name":"bms_ah_set","description":"write state of charge, use with caution","id":22,"endian":"little","repeat":0,"offset":0,"data":[{"name":"ah","type":"uint32_t","units":"","conversion":0.00001,"bits":null}]},{"name":"bms_wh_set","description":"write state of charge, use with caution","id":23,"endian":"little","repeat":0,"offset":0,"data":[{"name":"wh","type":"uint32_t","units":"","conversion":0.00001,"bits":null}]},{"name":"bms_kill","description":"packet to cause BMS kill","id":26,"endian":"little","repeat":0,"offset":0,"data":[{"name":"kill_type","type":"bitfield","units":"","conversion":0,"bits":[{"name":"KILL_HARD"}]}]},{"name":"telemetry_rtc_reset","description":"Reset telemetry's real-time clock (RTC).","id":1792,"endian":"","repeat":0,"offset":0,"data":[{"name":"year","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"month","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"day","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"hour","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"minute","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"second","type":"uint8_t","units":"","conversion":0,"bits":null}]},{"name":"wsr_identification","description":"WS RIGHT Identification Information","id":320,"endian":"little","repeat":0,"offset":0,"data":[{"name":"tritium_id","type":"uint32_t","units":"","conversion":0,"bits":null},{"name":"serial_number","type":"uint32_t","units":"","conversion":0,"bits":null}]},{"name":"wsr_status_information","description":"WS RIGHT Status Information","id":321,"endian":"little","repeat":0,"offset":0,"data":[{"name":"limit_flags","type":"bitfield","units":"","conversion":0,"bits":[{"name":"output_voltage_pwm"},{"name":"motor_current"},{"name":"velocity"},{"name":"bus_current"},{"name":"bus_voltage_upper_limit"},{"name":"bus_voltage_lower_limit"},{"name":"ipm_temperature_or_motor_temperature"},{"name":"reserved"}]},{"name":"limit_flags_reserved","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"error_flags_0","type":"bitfield","units":"","conversion":0,"bits":[{"name":"hardware_over_current"},{"name":"software_over_current"},{"name":"dc_bus_over_voltage"},{"name":"bad_motor_position_hall_sequence"},{"name":"watchdog_caused_last_reset"},{"name":"config_read_error"},{"name":"lv_rail_under_voltage_lock_out"},{"name":"desaturation_fault"}]},{"name":"error_flags_1","type":"bitfield","units":"","conversion":0,"bits":[{"name":"motor_over_speed"},{"name":"reserved_9"},{"name":"reserved_10"},{"name":"reserved_11"},{"name":"reserved_12"},{"name":"reserved_13"},{"name":"reserved_14"},{"name":"reserved_15"}]},{"name":"active_motor","type":"uint16_t","units":"","conversion":0,"bits":null},{"name":"reserved","type":"uint16_t","units":"","conversion":0,"bits":null}]},{"name":"wsr_bus_measurement","description":"WS RIGHT Bus Measurement","id":322,"endian":"little","repeat":0,"offset":0,"data":[{"name":"bus_voltage","type":"float","units":"V","conversion":0,"bits":null},{"name":"bus_current","type":"float","units":"A","conversion":0,"bits":null}]},{"name":"wsr_velocity","description":"WS RIGHT Velocity Measurement","id":323,"endian":"little","repeat":0,"offset":0,"data":[{"name":"motor_velocity","type":"float","units":"rpm","conversion":0,"bits":null},{"name":"vehicle_velocity","type":"float","units":"m/s","conversion":0,"bits":null}]},{"name":"wsr_phase_current","description":"WS RIGHT Phase Current Measurement","id":324,"endian":"little","repeat":0,"offset":0,"data":[{"name":"phase_b_current","type":"float","units":"A rms","conversion":0,"bits":null},{"name":"phase_c_current","type":"float","units":"A rms","conversion":0,"bits":null}]},{"name":"wsr_motor_voltage_vector","description":"WS RIGHT Motor Voltage Vector Measurement","id":325,"endian":"little","repeat":0,"offset":0,"data":[{"name":"vq","type":"float","units":"V","conversion":0,"bits":null},{"name":"vd","type":"float","units":"V","conversion":0,"bits":null}]},{"name":"wsr_motor_current_vector","description":"WS RIGHT Motor Current Vector Measurement","id":326,"endian":"little","repeat":0,"offset":0,"data":[{"name":"iq","type":"float","units":"A","conversion":0,"bits":null},{"name":"id","type":"float","units":"A","conversion":0,"bits":null}]},{"name":"wsr_motor_backemf","description":"WS RIGHT Motor BackEMF Measurement / Prediction","id":327,"endian":"little","repeat":0,"offset":0,"data":[{"name":"bemfq","type":"float","units":"V","conversion":0,"bits":null},{"name":"bemfd","type":"float","units":"V","conversion":0,"bits":null}]},{"name":"wsr_15_165_voltage_rail","description":"WS RIGHT 15 and 1.65 Voltage Rail Measurement","id":328,"endian":"little","repeat":0,"offset":0,"data":[{"name":"reference_165v","type":"float","units":"V","conversion":0,"bits":null},{"name":"supply_15v","type":"float","units":"V","conversion":0,"bits":null}]},{"name":"wsr_25_12_voltage_rail","description":"WS RIGHT 2.5V and 1.2V Voltage Rail Measurement","id":329,"endian":"little","repeat":0,"offset":0,"data":[{"name":"supply_12v","type":"float","units":"V","conversion":0,"bits":null},{"name":"supply_25v","type":"float","units":"V","conversion":0,"bits":null}]},{"name":"wsr_heatsink_motor_temp","description":"WS RIGHT Heat-sink \u0026 Motor Temperature Measurement","id":331,"endian":"little","repeat":0,"offset":0,"data":[{"name":"motor_temp","type":"float","units":"C","conversion":0,"bits":null},{"name":"heatsink_temp","type":"float","units":"C","conversion":0,"bits":null}]},{"name":"wsr_dsp_board_temp","description":"WS RIGHT DPS Board Temperature Measurement","id":332,"endian":"little","repeat":0,"offset":0,"data":[{"name":"dsp_board_temp","type":"float","units":"C","conversion":0,"bits":null},{"name":"reserved","type":"float","units":"C","conversion":0,"bits":null}]},{"name":"wsr_reserved","description":"WS RIGHT Reserved","id":333,"endian":"little","repeat":0,"offset":0,"data":[{"name":"reserved0","type":"float","units":"","conversion":0,"bits":null},{"name":"reserved1","type":"float","units":"","conversion":0,"bits":null}]},{"name":"wsr_odometer_bus_amphours_measurement","description":"WS RIGHT Odometer and Bus AmpHours Measurement","id":334,"endian":"little","repeat":0,"offset":0,"data":[{"name":"odometer","type":"float","units":"m","conversion":0,"bits":null},{"name":"dc_bus_amphours","type":"float","units":"Ah","conversion":0,"bits":null}]},{"name":"wsr_slip_speed_measurement","description":"WS RIGHT Slip Speed Measurement","id":343,"endian":"little","repeat":0,"offset":0,"data":[{"name":"reserved","type":"float","units":"C","conversion":0,"bits":null},{"name":"slip_speed","type":"float","units":"Hz","conversion":0,"bits":null}]},{"name":"wsl_identification","description":"WS LEFT Identification Information","id":256,"endian":"little","repeat":0,"offset":0,"data":[{"name":"tritium_id","type":"uint32_t","units":"","conversion":0,"bits":null},{"name":"serial_number","type":"uint32_t","units":"","conversion":0,"bits":null}]},{"name":"wsl_status_information","description":"WS LEFT Status Information","id":257,"endian":"little","repeat":0,"offset":0,"data":[{"name":"limit_flags","type":"bitfield","units":"","conversion":0,"bits":[{"name":"output_voltage_pwm"},{"name":"motor_current"},{"name":"velocity"},{"name":"bus_current"},{"name":"bus_voltage_upper_limit"},{"name":"bus_voltage_lower_limit"},{"name":"ipm_temperature_or_motor_temperature"},{"name":"reserved"}]},{"name":"limit_flags_reserved","type":"uint8_t","units":"","conversion":0,"bits":null},{"name":"error_flags_0","type":"bitfield","units":"","conversion":0,"bits":[{"name":"hardware_over_current"},{"name":"software_over_current"},{"name":"dc_bus_over_voltage"},{"name":"bad_motor_position_hall_sequence"},{"name":"watchdog_caused_last_reset"},{"name":"config_read_error"},{"name":"lv_rail_under_voltage_lock_out"},{"name":"desaturation_fault"}]},{"name":"error_flags_1","type":"bitfield","units":"","conversion":0,"bits":[{"name":"motor_over_speed"},{"name":"reserved_9"},{"name":"reserved_10"},{"name":"reserved_11"},{"name":"reserved_12"},{"name":"reserved_13"},{"name":"reserved_14"},{"name":"reserved_15"}]},{"name":"active_motor","type":"uint16_t","units":"","conversion":0,"bits":null},{"name":"reserved","type":"uint16_t","units":"","conversion":0,"bits":null}]},{"name":"wsl_bus_measurement","description":"WS LEFT Bus Measurement","id":258,"endian":"little","repeat":0,"offset":0,"data":[{"name":"bus_voltage","type":"float","units":"V","conversion":0,"bits":null},{"name":"bus_current","type":"float","units":"A","conversion":0,"bits":null}]},{"name":"wsl_velocity","description":"WS LEFT Velocity Measurement","id":259,"endian":"little","repeat":0,"offset":0,"data":[{"name":"motor_velocity","type":"float","units":"rpm","conversion":0,"bits":null},{"name":"vehicle_velocity","type":"float","units":"m/s","conversion":0,"bits":null}]},{"name":"wsl_phase_current","description":"WS LEFT Phase Current Measurement","id":260,"endian":"little","repeat":0,"offset":0,"data":[{"name":"phase_b_current","type":"float","units":"A rms","conversion":0,"bits":null},{"name":"phase_c_current","type":"float","units":"A rms","conversion":0,"bits":null}]},{"name":"wsl_motor_voltage_vector","description":"WS LEFT Motor Voltage Vector Measurement","id":261,"endian":"little","repeat":0,"offset":0,"data":[{"name":"vq","type":"float","units":"V","conversion":0,"bits":null},{"name":"vd","type":"float","units":"V","conversion":0,"bits":null}]},{"name":"wsl_motor_current_vector","description":"WS LEFT Motor Current Vector Measurement","id":262,"endian":"little","repeat":0,"offset":0,"data":[{"name":"iq","type":"float","units":"A","conversion":0,"bits":null},{"name":"id","type":"float","units":"A","conversion":0,"bits":null}]},{"name":"wsl_motor_backemf","description":"WS LEFT Motor BackEMF Measurement / Prediction","id":263,"endian":"little","repeat":0,"offset":0,"data":[{"name":"bemfq","type":"float","units":"V","conversion":0,"bits":null},{"name":"bemfd","type":"float","units":"V","conversion":0,"bits":null}]},{"name":"wsl_15_165_voltage_rail","description":"WS LEFT 15 and 1.65 Voltage Rail Measurement","id":264,"endian":"little","repeat":0,"offset":0,"data":[{"name":"reference_165v","type":"float","units":"V","conversion":0,"bits":null},{"name":"supply_15v","type":"float","units":"V","conversion":0,"bits":null}]},{"name":"wsl_25_12_voltage_rail","description":"WS LEFT 2.5V and 1.2V Voltage Rail Measurement","id":265,"endian":"little","repeat":0,"offset":0,"data":[{"name":"supply_12v","type":"float","units":"V","conversion":0,"bits":null},{"name":"supply_25v","type":"float","units":"V","conversion":0,"bits":null}]},{"name":"wsl_heatsink_motor_temp","description":"WS LEFT Heat-sink \u0026 Motor Temperature Measurement","id":267,"endian":"little","repeat":0,"offset":0,"data":[{"name":"motor_temp","type":"float","units":"C","conversion":0,"bits":null},{"name":"heatsink_temp","type":"float","units":"C","conversion":0,"bits":null}]},{"name":"wsl_dsp_board_temp","description":"WS LEFT DPS Board Temperature Measurement","id":268,"endian":"little","repeat":0,"offset":0,"data":[{"name":"dsp_board_temp","type":"float","units":"C","conversion":0,"bits":null},{"name":"reserved","type":"float","units":"C","conversion":0,"bits":null}]},{"name":"wsl_odometer_bus_amphours_measurement","description":"WS LEFT Odometer and Bus AmpHours Measurement","id":270,"endian":"little","repeat":0,"offset":0,"data":[{"name":"odometer","type":"float","units":"m","conversion":0,"bits":null},{"name":"dc_bus_amphours","type":"float","units":"Ah","conversion":0,"bits":null}]},{"name":"wsl_reserved","description":"WS LEFT Reserved","id":269,"endian":"little","repeat":0,"offset":0,"data":[{"name":"reserved0","type":"float","units":"","conversion":0,"bits":null},{"name":"reserved1","type":"float","units":"","conversion":0,"bits":null}]},{"name":"wsl_slip_speed_measurement","description":"WS LEFT Slip Speed Measurement","id":279,"endian":"little","repeat":0,"offset":0,"data":[{"name":"reserved","type":"float","units":"C","conversion":0,"bits":null},{"name":"slip_speed","type":"float","units":"Hz","conversion":0,"bits":null}]}],"boards":[{"name":"bms","transmit":["bms_measurement","bms_capacity","bms_charger_response","battery_status","bms_kill_reason","bms_module_min_max","bms_soc","bms_currentlimit","bms_fan_info","bms_module"],"receive":["bms_kill","bms_wh_set","bms_ah_set","bms_set_min_fan_speed","charger_bms_request"]},{"name":"charger","transmit":["charger_state","charger_bms_request","charger_power"],"receive":["bms_charger_response","battery_status","bms_module_min_max","bms_measurement","charger_current_voltage","bms_capacity"]},{"name":"flight_computer","transmit":["tracker_enable","vision_turn_signals_command","vision_brake_lights_command","vision_headlights_command","vision_horn_command","tritium_motor_drive","steering_button_colors_1","steering_button_colors_2","tritium_reset","dashboard_pedal_percentages","car_state","car_speed","dashboard_pedal_fault","flight_computer_hv_board_disconnect_counts","flight_computer_lv_board_disconnect_counts","flight_computer_internal_state","power_to_drive","array_power","distance_traveled"],"receive":["bms_module_min_max","charger_state","steering_press_count_1","steering_press_count_2","wsl_velocity","wsr_velocity","steering_horn","bms_kill_reason","bms_measurement","tracker_data","vision_status_front","vision_status_rear","wsl_odometer_bus_amphours_measurement","wsr_odometer_bus_amphours_measurement"]},{"name":"g4_example","transmit":["vision_status_front","vision_headlights_command","tracker_data","demo_packet"],"receive":["vision_turn_signals_command","vision_brake_lights_command","vision_headlights_command","vision_horn_command","demo_packet"]},{"name":"lights","transmit":["vision_status_front","vision_status_rear","lights_front_id","lights_back_id"],"receive":["vision_turn_signals_command","vision_brake_lights_command","vision_headlights_command","bms_kill_reason"]},{"name":"skylab2_demo","transmit":["vision_status_front","vision_headlights_command","tracker_data"],"receive":["vision_turn_signals_command","vision_brake_lights_command","vision_headlights_command","vision_horn_command"]},{"name":"steering","transmit":["steering_press_count_1","steering_press_count_2","steering_horn"],"receive":["steering_button_colors_1","steering_button_colors_2"]},{"name":"vision","transmit":["vision_status_front","vision_id"],"receive":["vision_turn_signals_command","vision_brake_lights_command","vision_headlights_command","vision_horn_command","vision_array_latches_command","vision_rearview_command"]}]}` +const SkylabDefinitions = `{"Packets":[{"Name":"bms_measurement","Description":"Voltages for main battery and aux pack","Id":16,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"battery_voltage","Type":"uint16_t","Units":"V","Conversion":0.01,"Bits":null},{"Name":"aux_voltage","Type":"uint16_t","Units":"V","Conversion":0.001,"Bits":null},{"Name":"current","Type":"float","Units":"A","Conversion":1,"Bits":null}]},{"Name":"battery_status","Description":"Status bits for the battery","Id":17,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"battery_state","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"startup"},{"Name":"precharge"},{"Name":"discharging"},{"Name":"lv_only"},{"Name":"charging"},{"Name":"wall_charging"},{"Name":"killed"}]},{"Name":"contactor_state","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"battery_high_contactor"},{"Name":"battery_low_contactor"},{"Name":"battery_vicor_contactor"},{"Name":"battery_pre_contactor"},{"Name":"battery_high2_contactor"},{"Name":"battery_low2_contactor"},{"Name":"charger_high_contactor"},{"Name":"charger_pre_contactor"}]},{"Name":"lv_channel_status","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"aux_fault"},{"Name":"main_fault"},{"Name":"aux_power_valid"},{"Name":"main_power_valid"},{"Name":"aux_power_active"},{"Name":"main_power_active"}]},{"Name":"lv_control_status","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"aux_vicor_enable"},{"Name":"bat_vicor_enable"},{"Name":"aux_relay_held"},{"Name":"aux_ref_enable"},{"Name":"aux_charging_enable"},{"Name":"kill_hv"},{"Name":"kill_lv"},{"Name":"start_button"}]},{"Name":"pack_choice","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"large_pack"},{"Name":"small_pack"}]}]},{"Name":"bms_kill_reason","Description":"Information for when the car kills","Id":18,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"reason1","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"OVERVOLT"},{"Name":"UNDERVOLT"},{"Name":"OVERTEMP"},{"Name":"TEMP_DISCONNECT"},{"Name":"COMM_FAIL"}]},{"Name":"reason2","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"HARDWARE"},{"Name":"KILL_PACKET"},{"Name":"UKNOWN"},{"Name":"OVERCURRENT"},{"Name":"PRECHARGE_FAIL"},{"Name":"AUX_OVER_UNDER"},{"Name":"AUX_OVERTEMP"}]},{"Name":"module","Type":"uint16_t","Units":"","Conversion":0,"Bits":null},{"Name":"value","Type":"float","Units":"","Conversion":0,"Bits":null}]},{"Name":"bms_module_min_max","Description":"min and max cell voltages and temperatures","Id":19,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"module_max_temp","Type":"int16_t","Units":"C","Conversion":0.01,"Bits":null},{"Name":"module_min_temp","Type":"int16_t","Units":"C","Conversion":0.01,"Bits":null},{"Name":"module_max_voltage","Type":"uint16_t","Units":"V","Conversion":0.001,"Bits":null},{"Name":"module_min_voltage","Type":"uint16_t","Units":"V","Conversion":0.001,"Bits":null}]},{"Name":"bms_soc","Description":"State of charge","Id":20,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"soc","Type":"float","Units":"","Conversion":1,"Bits":null}]},{"Name":"bms_capacity","Description":"State of charge","Id":21,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"Ah","Type":"float","Units":"","Conversion":1,"Bits":null},{"Name":"Wh","Type":"float","Units":"","Conversion":1,"Bits":null}]},{"Name":"bms_currentlimit","Description":"reports BP params for current","Id":24,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"current_max","Type":"int16_t","Units":"A","Conversion":0.01,"Bits":null},{"Name":"current_min","Type":"int16_t","Units":"A","Conversion":0.01,"Bits":null}]},{"Name":"bms_fan_info","Description":"BP Fans","Id":25,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"fan1","Type":"uint16_t","Units":"RPM","Conversion":1,"Bits":null},{"Name":"fan2","Type":"uint16_t","Units":"RPM","Conversion":1,"Bits":null},{"Name":"fan3","Type":"uint16_t","Units":"RPM","Conversion":1,"Bits":null},{"Name":"fan4","Type":"uint16_t","Units":"RPM","Conversion":1,"Bits":null}]},{"Name":"bms_set_min_fan_speed","Description":"packet which sets a minimum fan speed of BMS for a specific time frame in seconds","Id":27,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"fan_percentage","Type":"float","Units":"percent","Conversion":0,"Bits":null},{"Name":"time","Type":"uint16_t","Units":"s","Conversion":0,"Bits":null}]},{"Name":"bms_module","Description":"Voltage and temperature for a single module","Id":64,"Endian":"little","Extended":false,"Repeat":36,"Offset":1,"Data":[{"Name":"voltage","Type":"float","Units":"V","Conversion":1,"Bits":null},{"Name":"temperature","Type":"float","Units":"C","Conversion":1,"Bits":null}]},{"Name":"bms_charger_response","Description":"Response packet from BMS for indicating whether BMS is ready for charging","Id":117,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"response_flags","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"charging_ready"}]}]},{"Name":"chassis_isolation_fault","Description":"chassiss is not isolated from the battery","Id":56,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"fault_detected","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"isolation_fault"}]}]},{"Name":"bms_imd_info","Description":"information from chassis isolation","Id":55,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"d_imc_r_iso","Type":"uint16_t","Units":"","Conversion":0,"Bits":null},{"Name":"d_imc_status_1","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"isolation_fault"},{"Name":"chassis_fault"},{"Name":"system_failure"},{"Name":"calibration_running"},{"Name":"self_test_running"},{"Name":"isolation_warning"},{"Name":"reserved"},{"Name":"reserved_2"}]},{"Name":"d_imc_status_2","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"reserved"},{"Name":"reserved_2"},{"Name":"reserved_3"},{"Name":"reserved_4"},{"Name":"reserved_5"},{"Name":"reserved_6"},{"Name":"reserved_7"},{"Name":"reserved_8"}]},{"Name":"d_vifc_status_1","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"insulation_measurment"},{"Name":"imc_connectivity_not_implemented"},{"Name":"imc_alive_satus_detection"},{"Name":"reserved"},{"Name":"vifc_command_not_implemented"},{"Name":"reserved_2"},{"Name":"reserved_3"},{"Name":"reserved_4"}]},{"Name":"d_vifc_status_2","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"insulation_resistance_value"},{"Name":"reserved"},{"Name":"reserved_2"},{"Name":"reserved_3"},{"Name":"imc_self_test_overAll"},{"Name":"imc_self_test_parameterConfig"},{"Name":"reserved_4"},{"Name":"reserved_5"}]}]},{"Name":"dashboard_pedal_percentages","Description":"ADC values from the brake and accelerator pedals.","Id":656,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"accel_pedal_value","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"brake_pedal_value","Type":"uint8_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"car_state","Description":"Car gear. Forward, neutral, reverse, etc.","Id":657,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"state","Type":"uint8_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"dashboard_pedal_fault","Description":"Target speed that the driver should maintain.","Id":658,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"brake_fault_count","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"accel_fault_count","Type":"uint8_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"dashboard_system_timeout_test","Description":"Exposes whether each system that dashboard is supposed to listen for packets from has sent a packet. Used for testing.","Id":665,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"flag_set_0","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"steering_disconnected"},{"Name":"vision_front_disconnected"},{"Name":"vision_rear_disconnected"},{"Name":"telemetry_disconnected"},{"Name":"wsl_disconnected"},{"Name":"wsr_disconnected"},{"Name":"front_mppt_disconnected"},{"Name":"rear_mppt_disconnected"}]}]},{"Name":"car_speed","Description":"speed of car in meters per second","Id":666,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"speed","Type":"float","Units":"","Conversion":0,"Bits":null}]},{"Name":"flight_computer_lv_board_disconnect_counts","Description":"Number of times a board hasn't been heard from within the allowed timeout.","Id":667,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"front_lights","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"rear_lights","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"steering","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"vision","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"driver_display","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"center_console","Type":"uint8_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"flight_computer_hv_board_disconnect_counts","Description":"Number of times a board hasn't been heard from within the allowed timeout.","Id":668,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"bms","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"charger","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"wsl","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"wsr","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"mppt_front","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"mppt_rear","Type":"uint8_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"flight_computer_internal_state","Description":"internal bools","Id":669,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"bms","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"battery_kill"},{"Name":"cells_in_charging_threshold"},{"Name":"first_packet_received"}]},{"Name":"charger","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"proximity_detected"}]},{"Name":"photon3","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"enable"}]},{"Name":"wavesculptor","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"sending_reset"},{"Name":"regen_enable"}]},{"Name":"internal","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"accel_pedal_disconnect"},{"Name":"brake_pedal_disconnect"}]}]},{"Name":"power_to_drive","Description":"calculated power required to drive the vehicle","Id":414,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"moving_average_100","Type":"int16_t","Units":"","Conversion":0,"Bits":null},{"Name":"moving_average_1k","Type":"int16_t","Units":"","Conversion":0,"Bits":null},{"Name":"moving_average_10k","Type":"int16_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"array_power","Description":"array power calculated from current and voltage measurements","Id":415,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"front_array_channel_0","Type":"uint16_t","Units":"","Conversion":0,"Bits":null},{"Name":"front_array_channel_1","Type":"uint16_t","Units":"","Conversion":0,"Bits":null},{"Name":"rear_array_channel_0","Type":"uint16_t","Units":"","Conversion":0,"Bits":null},{"Name":"rear_array_channel_1","Type":"uint16_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"array_energy","Description":"cumulative energy received from the array","Id":281,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"energy","Type":"float","Units":"Joule","Conversion":0,"Bits":null}]},{"Name":"array_energy_reset","Description":"resets cumulative energy received from the array","Id":288,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"energy","Type":"float","Units":"Joule","Conversion":0,"Bits":null}]},{"Name":"vision_turn_signals_command","Description":"Command to have the vision board illuminate or turn off left, right, or both turn signals","Id":688,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"lights","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"left_turn_signal"},{"Name":"right_turn_signal"},{"Name":"spare_1"},{"Name":"spare_2"},{"Name":"spare_3"}]}]},{"Name":"vision_brake_lights_command","Description":"Command to have the vision board illuminate or turn off the brake lights","Id":689,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"lights","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"brake_lights"},{"Name":"spare_1"},{"Name":"spare_2"},{"Name":"spare_3"}]}]},{"Name":"vision_headlights_command","Description":"Command to have the vision board illuminate or turn off the headlights and high beams","Id":690,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"lights","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"headlights"},{"Name":"high_beams"},{"Name":"spare_1"},{"Name":"spare_2"},{"Name":"spare_3"}]},{"Name":"brightness","Type":"float","Units":"","Conversion":0,"Bits":null}]},{"Name":"vision_horn_command","Description":"Command the vision board honk the horn, must be repeatedly sent otherwise the vision board will stop honking after a bit. See high_power.h for details.","Id":691,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"horn","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"horn"},{"Name":"spare"}]}]},{"Name":"vision_array_latches_command","Description":"Command the vision board to open the array latches","Id":692,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"array_latches","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"array_front"},{"Name":"array_rear"}]}]},{"Name":"vision_rearview_command","Description":"Command the vision board turn on the rear view cameras","Id":693,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"cameras","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"left"},{"Name":"right"},{"Name":"rear"}]}]},{"Name":"tracker_enable","Description":"Enables/disables power trackers. Use 0x610 for the channel transmitting the data packet on 0x600, 0x611 for 0x601, et cetera. Sending 1 in the enable byte turns the tracker on; sending 0 turns it off.","Id":1552,"Endian":"little","Extended":false,"Repeat":6,"Offset":1,"Data":[{"Name":"enable","Type":"uint8_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"distance_traveled","Description":"distance of wavesculptor odometer","Id":413,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"trip_distance","Type":"float","Units":"m","Conversion":0,"Bits":null}]},{"Name":"charger_state","Description":"Notifies whether the J1772 cable is plugged.","Id":1395,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"state_flags","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"charger_plugged"}]},{"Name":"charger_max_temp","Type":"uint16_t","Units":"C","Conversion":0.001,"Bits":null},{"Name":"fault","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"CHARGER_OVERVOLT"},{"Name":"CHARGER_OVERTEMP"},{"Name":"CHARGER_CAN_TIMEOUT"},{"Name":"BATTERY_HV_KILL"},{"Name":"BATTERY_UNDERVOLT"},{"Name":"BATTERY_OVERVOLT"},{"Name":"BATTERY_CELL_OVERTEMP"},{"Name":"BATTERY_CAN_TIMEOUT"}]},{"Name":"charging_current","Type":"float","Units":"A","Conversion":0,"Bits":null}]},{"Name":"charger_bms_request","Description":"Request packet for sending contactor commands from the charger to BP.","Id":116,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"request_flags","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"charging_requested"}]}]},{"Name":"charger_current_voltage","Description":"Packet to request charging current/voltage set","Id":1398,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"max_current","Type":"float","Units":"A","Conversion":0,"Bits":null},{"Name":"max_capacity","Type":"float","Units":"kWh","Conversion":0,"Bits":null}]},{"Name":"charger_power","Description":"Outputs the amount of power that the chargers are delivering.","Id":1399,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"power","Type":"float","Units":"W","Conversion":0,"Bits":null}]},{"Name":"thunderstruck_control_message","Description":"Control packet for thunderstruck chargers","Id":417677348,"Endian":"little","Extended":true,"Repeat":0,"Offset":0,"Data":[{"Name":"Enable","Type":"uint8_t","Units":"V","Conversion":1,"Bits":null},{"Name":"CHARGE_VOLTAGE","Type":"uint16_t","Units":"V","Conversion":1,"Bits":null},{"Name":"CHARGE_CURRENT","Type":"uint16_t","Units":"V","Conversion":1,"Bits":null},{"Name":"LED_BLINK_PATTERN","Type":"uint8_t","Units":"V","Conversion":1,"Bits":null},{"Name":"RESERVED","Type":"uint16_t","Units":"V","Conversion":1,"Bits":null}]},{"Name":"vision_status_front","Description":"Status of the front vision board outputs","Id":694,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"lights","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"left_turn_signal"},{"Name":"right_turn_signal"},{"Name":"brake_lights"},{"Name":"headlights"},{"Name":"high_beams"},{"Name":"spare_1"},{"Name":"spare_2"},{"Name":"spare_3"}]},{"Name":"horn","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"horn"},{"Name":"spare"}]},{"Name":"cameras","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"left"},{"Name":"right"},{"Name":"rear"}]},{"Name":"array_latches","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"array_front_0"},{"Name":"array_front_1"},{"Name":"array_rear_0"},{"Name":"array_rear_1"}]}]},{"Name":"vision_status_rear","Description":"Status of the rear vision board outputs","Id":695,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"lights","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"left_turn_signal"},{"Name":"right_turn_signal"},{"Name":"brake_lights"},{"Name":"headlights"},{"Name":"high_beams"},{"Name":"spare_1"},{"Name":"spare_2"},{"Name":"spare_3"}]},{"Name":"horn","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"horn"},{"Name":"spare"}]},{"Name":"cameras","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"left"},{"Name":"right"},{"Name":"rear"}]},{"Name":"array_latches","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"array_front_0"},{"Name":"array_front_1"},{"Name":"array_rear_0"},{"Name":"array_rear_1"}]}]},{"Name":"lights_front_id","Description":"Unique identification packet for front lights board","Id":768,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"board_id","Type":"uint16_t","Units":"","Conversion":0,"Bits":null},{"Name":"mcu_temp","Type":"int16_t","Units":"C","Conversion":0.01,"Bits":null},{"Name":"bus_voltage","Type":"uint16_t","Units":"V","Conversion":0.001,"Bits":null},{"Name":"fault_code","Type":"uint16_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"lights_back_id","Description":"Unique identification packet for back lights board","Id":769,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"board_id","Type":"uint16_t","Units":"","Conversion":0,"Bits":null},{"Name":"mcu_temp","Type":"int16_t","Units":"C","Conversion":0.01,"Bits":null},{"Name":"bus_voltage","Type":"uint16_t","Units":"V","Conversion":0.001,"Bits":null},{"Name":"fault_code","Type":"uint16_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"vision_id","Description":"Unique identification packet for vision","Id":770,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"board_id","Type":"uint16_t","Units":"","Conversion":0,"Bits":null},{"Name":"mcu_temp","Type":"int16_t","Units":"C","Conversion":0.01,"Bits":null},{"Name":"bus_voltage","Type":"uint16_t","Units":"V","Conversion":0.001,"Bits":null},{"Name":"fault_code","Type":"uint16_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"steering_press_count_1","Description":"Shows whether each button has been toggled an even (\"on\") or odd (\"off\") number of times.","Id":576,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"button0","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button1","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button2","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button3","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button4","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button5","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button6","Type":"uint8_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"steering_press_count_2","Description":"Shows whether each button has been toggled an even (\"on\") or odd (\"off\") number of times.","Id":592,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"button7","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button8","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button9","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button10","Type":"uint8_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"steering_button_colors_1","Description":"This packet controls each button's color. Each byte is a hex color code.","Id":577,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"button0","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button1","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button2","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button3","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button4","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button5","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button6","Type":"uint8_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"steering_button_colors_2","Description":"This packet controls each button's color. Each byte is a hex color code.","Id":593,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"button7","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button8","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button9","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"button10","Type":"uint8_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"steering_horn","Description":"This packet controls the state of the horn.","Id":578,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"horn","Type":"uint8_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"thunderstruck_status_message","Description":"Status packet for thunderstruck chargers","Id":418063424,"Endian":"little","Extended":true,"Repeat":0,"Offset":0,"Data":[{"Name":"STATUS_FLAGS","Type":"uint8_t","Units":"V","Conversion":1,"Bits":null},{"Name":"CHARGE_FLAGS","Type":"uint8_t","Units":"V","Conversion":1,"Bits":null},{"Name":"OUTPUT_VOLTAGE","Type":"uint16_t","Units":"V","Conversion":1,"Bits":null},{"Name":"OUTPUT_CURRENT","Type":"uint16_t","Units":"V","Conversion":1,"Bits":null},{"Name":"CHARGER_TEMP","Type":"uint8_t","Units":"V","Conversion":1,"Bits":null},{"Name":"RESERVED","Type":"uint8_t","Units":"V","Conversion":1,"Bits":null}]},{"Name":"tracker_data","Description":"Tracker data. Each channel transmits on a specific ID, which should be specified along with the tracker, most likely 0x600-0x603.","Id":1536,"Endian":"little","Extended":false,"Repeat":6,"Offset":1,"Data":[{"Name":"array_voltage","Type":"uint16_t","Units":"V","Conversion":0.01,"Bits":null},{"Name":"array_current","Type":"uint16_t","Units":"A","Conversion":0.001,"Bits":null},{"Name":"battery_voltage","Type":"uint16_t","Units":"V","Conversion":0.01,"Bits":null},{"Name":"temperature","Type":"uint16_t","Units":"C","Conversion":0.01,"Bits":null}]},{"Name":"tritium_motor_drive_l","Description":"Tritium Motor Drive Command","Id":289,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"motor_velocity","Type":"float","Units":"","Conversion":0,"Bits":null},{"Name":"motor_current","Type":"float","Units":"","Conversion":0,"Bits":null}]},{"Name":"tritium_motor_power_l","Description":"Tritium Motor Power Command","Id":290,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"reserved","Type":"float","Units":"","Conversion":0,"Bits":null},{"Name":"bus_current","Type":"float","Units":"","Conversion":0,"Bits":null}]},{"Name":"tritium_reset_l","Description":"Tritium Reset Command","Id":291,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"unused1","Type":"float","Units":"","Conversion":0,"Bits":null},{"Name":"unused2","Type":"float","Units":"","Conversion":0,"Bits":null}]},{"Name":"tritium_motor_drive_r","Description":"Tritium Motor Drive Command","Id":353,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"motor_velocity","Type":"float","Units":"","Conversion":0,"Bits":null},{"Name":"motor_current","Type":"float","Units":"","Conversion":0,"Bits":null}]},{"Name":"tritium_motor_power_r","Description":"Tritium Motor Power Command","Id":354,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"reserved","Type":"float","Units":"","Conversion":0,"Bits":null},{"Name":"bus_current","Type":"float","Units":"","Conversion":0,"Bits":null}]},{"Name":"tritium_reset_r","Description":"Tritium Reset Command","Id":355,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"unused1","Type":"float","Units":"","Conversion":0,"Bits":null},{"Name":"unused2","Type":"float","Units":"","Conversion":0,"Bits":null}]},{"Name":"bms_ah_set","Description":"write state of charge, use with caution","Id":22,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"ah","Type":"uint32_t","Units":"","Conversion":0.00001,"Bits":null}]},{"Name":"bms_wh_set","Description":"write state of charge, use with caution","Id":23,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"wh","Type":"uint32_t","Units":"","Conversion":0.00001,"Bits":null}]},{"Name":"bms_kill","Description":"packet to cause BMS kill","Id":26,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"kill_type","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"KILL_HARD"}]}]},{"Name":"telemetry_rtc_reset","Description":"Reset telemetry's real-time clock (RTC).","Id":1792,"Endian":"","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"year","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"month","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"day","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"hour","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"minute","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"second","Type":"uint8_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"wsr_identification","Description":"WS RIGHT Identification Information","Id":320,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"tritium_id","Type":"uint32_t","Units":"","Conversion":0,"Bits":null},{"Name":"serial_number","Type":"uint32_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"wsr_status_information","Description":"WS RIGHT Status Information","Id":321,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"limit_flags","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"output_voltage_pwm"},{"Name":"motor_current"},{"Name":"velocity"},{"Name":"bus_current"},{"Name":"bus_voltage_upper_limit"},{"Name":"bus_voltage_lower_limit"},{"Name":"ipm_temperature_or_motor_temperature"},{"Name":"reserved"}]},{"Name":"limit_flags_reserved","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"error_flags_0","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"hardware_over_current"},{"Name":"software_over_current"},{"Name":"dc_bus_over_voltage"},{"Name":"bad_motor_position_hall_sequence"},{"Name":"watchdog_caused_last_reset"},{"Name":"config_read_error"},{"Name":"lv_rail_under_voltage_lock_out"},{"Name":"desaturation_fault"}]},{"Name":"error_flags_1","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"motor_over_speed"},{"Name":"reserved_9"},{"Name":"reserved_10"},{"Name":"reserved_11"},{"Name":"reserved_12"},{"Name":"reserved_13"},{"Name":"reserved_14"},{"Name":"reserved_15"}]},{"Name":"active_motor","Type":"uint16_t","Units":"","Conversion":0,"Bits":null},{"Name":"reserved","Type":"uint16_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"wsr_bus_measurement","Description":"WS RIGHT Bus Measurement","Id":322,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"bus_voltage","Type":"float","Units":"V","Conversion":0,"Bits":null},{"Name":"bus_current","Type":"float","Units":"A","Conversion":0,"Bits":null}]},{"Name":"wsr_velocity","Description":"WS RIGHT Velocity Measurement","Id":323,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"motor_velocity","Type":"float","Units":"rpm","Conversion":0,"Bits":null},{"Name":"vehicle_velocity","Type":"float","Units":"m/s","Conversion":0,"Bits":null}]},{"Name":"wsr_phase_current","Description":"WS RIGHT Phase Current Measurement","Id":324,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"phase_b_current","Type":"float","Units":"A rms","Conversion":0,"Bits":null},{"Name":"phase_c_current","Type":"float","Units":"A rms","Conversion":0,"Bits":null}]},{"Name":"wsr_motor_voltage_vector","Description":"WS RIGHT Motor Voltage Vector Measurement","Id":325,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"vq","Type":"float","Units":"V","Conversion":0,"Bits":null},{"Name":"vd","Type":"float","Units":"V","Conversion":0,"Bits":null}]},{"Name":"wsr_motor_current_vector","Description":"WS RIGHT Motor Current Vector Measurement","Id":326,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"iq","Type":"float","Units":"A","Conversion":0,"Bits":null},{"Name":"id","Type":"float","Units":"A","Conversion":0,"Bits":null}]},{"Name":"wsr_motor_backemf","Description":"WS RIGHT Motor BackEMF Measurement / Prediction","Id":327,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"bemfq","Type":"float","Units":"V","Conversion":0,"Bits":null},{"Name":"bemfd","Type":"float","Units":"V","Conversion":0,"Bits":null}]},{"Name":"wsr_15_165_voltage_rail","Description":"WS RIGHT 15 and 1.65 Voltage Rail Measurement","Id":328,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"reference_165v","Type":"float","Units":"V","Conversion":0,"Bits":null},{"Name":"supply_15v","Type":"float","Units":"V","Conversion":0,"Bits":null}]},{"Name":"wsr_25_12_voltage_rail","Description":"WS RIGHT 2.5V and 1.2V Voltage Rail Measurement","Id":329,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"supply_12v","Type":"float","Units":"V","Conversion":0,"Bits":null},{"Name":"supply_25v","Type":"float","Units":"V","Conversion":0,"Bits":null}]},{"Name":"wsr_heatsink_motor_temp","Description":"WS RIGHT Heat-sink \u0026 Motor Temperature Measurement","Id":331,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"motor_temp","Type":"float","Units":"C","Conversion":0,"Bits":null},{"Name":"heatsink_temp","Type":"float","Units":"C","Conversion":0,"Bits":null}]},{"Name":"wsr_dsp_board_temp","Description":"WS RIGHT DPS Board Temperature Measurement","Id":332,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"dsp_board_temp","Type":"float","Units":"C","Conversion":0,"Bits":null},{"Name":"reserved","Type":"float","Units":"C","Conversion":0,"Bits":null}]},{"Name":"wsr_reserved","Description":"WS RIGHT Reserved","Id":333,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"reserved0","Type":"float","Units":"","Conversion":0,"Bits":null},{"Name":"reserved1","Type":"float","Units":"","Conversion":0,"Bits":null}]},{"Name":"wsr_odometer_bus_amphours_measurement","Description":"WS RIGHT Odometer and Bus AmpHours Measurement","Id":334,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"odometer","Type":"float","Units":"m","Conversion":0,"Bits":null},{"Name":"dc_bus_amphours","Type":"float","Units":"Ah","Conversion":0,"Bits":null}]},{"Name":"wsr_slip_speed_measurement","Description":"WS RIGHT Slip Speed Measurement","Id":343,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"reserved","Type":"float","Units":"C","Conversion":0,"Bits":null},{"Name":"slip_speed","Type":"float","Units":"Hz","Conversion":0,"Bits":null}]},{"Name":"wsl_identification","Description":"WS LEFT Identification Information","Id":256,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"tritium_id","Type":"uint32_t","Units":"","Conversion":0,"Bits":null},{"Name":"serial_number","Type":"uint32_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"wsl_status_information","Description":"WS LEFT Status Information","Id":257,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"limit_flags","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"output_voltage_pwm"},{"Name":"motor_current"},{"Name":"velocity"},{"Name":"bus_current"},{"Name":"bus_voltage_upper_limit"},{"Name":"bus_voltage_lower_limit"},{"Name":"ipm_temperature_or_motor_temperature"},{"Name":"reserved"}]},{"Name":"limit_flags_reserved","Type":"uint8_t","Units":"","Conversion":0,"Bits":null},{"Name":"error_flags_0","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"hardware_over_current"},{"Name":"software_over_current"},{"Name":"dc_bus_over_voltage"},{"Name":"bad_motor_position_hall_sequence"},{"Name":"watchdog_caused_last_reset"},{"Name":"config_read_error"},{"Name":"lv_rail_under_voltage_lock_out"},{"Name":"desaturation_fault"}]},{"Name":"error_flags_1","Type":"bitfield","Units":"","Conversion":0,"Bits":[{"Name":"motor_over_speed"},{"Name":"reserved_9"},{"Name":"reserved_10"},{"Name":"reserved_11"},{"Name":"reserved_12"},{"Name":"reserved_13"},{"Name":"reserved_14"},{"Name":"reserved_15"}]},{"Name":"active_motor","Type":"uint16_t","Units":"","Conversion":0,"Bits":null},{"Name":"reserved","Type":"uint16_t","Units":"","Conversion":0,"Bits":null}]},{"Name":"wsl_bus_measurement","Description":"WS LEFT Bus Measurement","Id":258,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"bus_voltage","Type":"float","Units":"V","Conversion":0,"Bits":null},{"Name":"bus_current","Type":"float","Units":"A","Conversion":0,"Bits":null}]},{"Name":"wsl_velocity","Description":"WS LEFT Velocity Measurement","Id":259,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"motor_velocity","Type":"float","Units":"rpm","Conversion":0,"Bits":null},{"Name":"vehicle_velocity","Type":"float","Units":"m/s","Conversion":0,"Bits":null}]},{"Name":"wsl_phase_current","Description":"WS LEFT Phase Current Measurement","Id":260,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"phase_b_current","Type":"float","Units":"A rms","Conversion":0,"Bits":null},{"Name":"phase_c_current","Type":"float","Units":"A rms","Conversion":0,"Bits":null}]},{"Name":"wsl_motor_voltage_vector","Description":"WS LEFT Motor Voltage Vector Measurement","Id":261,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"vq","Type":"float","Units":"V","Conversion":0,"Bits":null},{"Name":"vd","Type":"float","Units":"V","Conversion":0,"Bits":null}]},{"Name":"wsl_motor_current_vector","Description":"WS LEFT Motor Current Vector Measurement","Id":262,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"iq","Type":"float","Units":"A","Conversion":0,"Bits":null},{"Name":"id","Type":"float","Units":"A","Conversion":0,"Bits":null}]},{"Name":"wsl_motor_backemf","Description":"WS LEFT Motor BackEMF Measurement / Prediction","Id":263,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"bemfq","Type":"float","Units":"V","Conversion":0,"Bits":null},{"Name":"bemfd","Type":"float","Units":"V","Conversion":0,"Bits":null}]},{"Name":"wsl_15_165_voltage_rail","Description":"WS LEFT 15 and 1.65 Voltage Rail Measurement","Id":264,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"reference_165v","Type":"float","Units":"V","Conversion":0,"Bits":null},{"Name":"supply_15v","Type":"float","Units":"V","Conversion":0,"Bits":null}]},{"Name":"wsl_25_12_voltage_rail","Description":"WS LEFT 2.5V and 1.2V Voltage Rail Measurement","Id":265,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"supply_12v","Type":"float","Units":"V","Conversion":0,"Bits":null},{"Name":"supply_25v","Type":"float","Units":"V","Conversion":0,"Bits":null}]},{"Name":"wsl_heatsink_motor_temp","Description":"WS LEFT Heat-sink \u0026 Motor Temperature Measurement","Id":267,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"motor_temp","Type":"float","Units":"C","Conversion":0,"Bits":null},{"Name":"heatsink_temp","Type":"float","Units":"C","Conversion":0,"Bits":null}]},{"Name":"wsl_dsp_board_temp","Description":"WS LEFT DPS Board Temperature Measurement","Id":268,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"dsp_board_temp","Type":"float","Units":"C","Conversion":0,"Bits":null},{"Name":"reserved","Type":"float","Units":"C","Conversion":0,"Bits":null}]},{"Name":"wsl_odometer_bus_amphours_measurement","Description":"WS LEFT Odometer and Bus AmpHours Measurement","Id":270,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"odometer","Type":"float","Units":"m","Conversion":0,"Bits":null},{"Name":"dc_bus_amphours","Type":"float","Units":"Ah","Conversion":0,"Bits":null}]},{"Name":"wsl_reserved","Description":"WS LEFT Reserved","Id":269,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"reserved0","Type":"float","Units":"","Conversion":0,"Bits":null},{"Name":"reserved1","Type":"float","Units":"","Conversion":0,"Bits":null}]},{"Name":"wsl_slip_speed_measurement","Description":"WS LEFT Slip Speed Measurement","Id":279,"Endian":"little","Extended":false,"Repeat":0,"Offset":0,"Data":[{"Name":"reserved","Type":"float","Units":"C","Conversion":0,"Bits":null},{"Name":"slip_speed","Type":"float","Units":"Hz","Conversion":0,"Bits":null}]}],"Boards":[{"Name":"bms","Transmit":["bms_measurement","bms_capacity","bms_charger_response","battery_status","bms_kill_reason","bms_module_min_max","bms_soc","bms_currentlimit","bms_fan_info","bms_module"],"Receive":["bms_kill","bms_wh_set","bms_ah_set","bms_set_min_fan_speed","charger_bms_request","chassis_isolation_fault"]},{"Name":"bridge_board","Transmit":["chassis_isolation_fault"],"Receive":["bms_imd_info","battery_status"]},{"Name":"charger","Transmit":["charger_state","charger_bms_request","charger_power","thunderstruck_control_message"],"Receive":["bms_charger_response","battery_status","bms_module_min_max","bms_measurement","charger_current_voltage","bms_capacity","thunderstruck_status_message"]},{"Name":"flight_computer","Transmit":["tracker_enable","vision_turn_signals_command","vision_brake_lights_command","vision_headlights_command","vision_horn_command","tritium_motor_drive_l","tritium_motor_drive_r","steering_button_colors_1","steering_button_colors_2","tritium_reset_l","tritium_reset_r","dashboard_pedal_percentages","car_state","car_speed","dashboard_pedal_fault","flight_computer_hv_board_disconnect_counts","flight_computer_lv_board_disconnect_counts","flight_computer_internal_state","power_to_drive","array_power","array_energy","distance_traveled"],"Receive":["bms_module_min_max","charger_state","steering_press_count_1","steering_press_count_2","wsl_velocity","wsr_velocity","steering_horn","bms_kill_reason","bms_measurement","tracker_data","vision_status_front","vision_status_rear","wsl_odometer_bus_amphours_measurement","array_energy_reset","wsr_odometer_bus_amphours_measurement"]},{"Name":"g4_example","Transmit":["vision_status_front","vision_headlights_command","demo_packet"],"Receive":["vision_turn_signals_command","vision_brake_lights_command","vision_headlights_command","vision_horn_command","demo_packet"]},{"Name":"lights","Transmit":["vision_status_front","vision_status_rear","lights_front_id","lights_back_id"],"Receive":["vision_turn_signals_command","vision_brake_lights_command","vision_headlights_command","bms_kill_reason"]},{"Name":"skylab2_demo","Transmit":["vision_status_front","vision_headlights_command"],"Receive":["vision_turn_signals_command","vision_brake_lights_command","vision_headlights_command","vision_horn_command"]},{"Name":"steering","Transmit":["steering_press_count_1","steering_press_count_2","steering_horn"],"Receive":["steering_button_colors_1","steering_button_colors_2"]},{"Name":"vision","Transmit":["vision_status_front","vision_id"],"Receive":["vision_turn_signals_command","vision_brake_lights_command","vision_headlights_command","vision_horn_command","vision_array_latches_command","vision_rearview_command"]}]}` diff --git a/skylab/skylab_gen_test.go b/skylab/skylab_gen_test.go index 68d4be6..a1a4c49 100644 --- a/skylab/skylab_gen_test.go +++ b/skylab/skylab_gen_test.go @@ -402,6 +402,78 @@ func TestJSONBmsChargerResponse(t *testing.T) { } +} +func TestMarshalUnmarshalChassisIsolationFault(t *testing.T) { + v := &ChassisIsolationFault{} + bin, err := v.MarshalPacket() + if err != nil { + t.Fatal(err) + } + err = v.UnmarshalPacket(bin) + if err != nil { + t.Fatal(err) + } +} + +func TestJSONChassisIsolationFault(t *testing.T) { + + v := &ChassisIsolationFault{} + + rawData, err := json.Marshal(v) + if err != nil { + t.Fatal(err) + } + + id, _ := v.CANId() + p, err := FromJson(id, rawData) + if err != nil { + t.Fatal(err) + } + + switch underlying := p.(type) { + case *ChassisIsolationFault: + break + default: + t.Fatalf("didn't match type: %T, %v", underlying, underlying) + } + + +} +func TestMarshalUnmarshalBmsImdInfo(t *testing.T) { + v := &BmsImdInfo{} + bin, err := v.MarshalPacket() + if err != nil { + t.Fatal(err) + } + err = v.UnmarshalPacket(bin) + if err != nil { + t.Fatal(err) + } +} + +func TestJSONBmsImdInfo(t *testing.T) { + + v := &BmsImdInfo{} + + rawData, err := json.Marshal(v) + if err != nil { + t.Fatal(err) + } + + id, _ := v.CANId() + p, err := FromJson(id, rawData) + if err != nil { + t.Fatal(err) + } + + switch underlying := p.(type) { + case *BmsImdInfo: + break + default: + t.Fatalf("didn't match type: %T, %v", underlying, underlying) + } + + } func TestMarshalUnmarshalDashboardPedalPercentages(t *testing.T) { v := &DashboardPedalPercentages{} @@ -762,6 +834,78 @@ func TestJSONArrayPower(t *testing.T) { } +} +func TestMarshalUnmarshalArrayEnergy(t *testing.T) { + v := &ArrayEnergy{} + bin, err := v.MarshalPacket() + if err != nil { + t.Fatal(err) + } + err = v.UnmarshalPacket(bin) + if err != nil { + t.Fatal(err) + } +} + +func TestJSONArrayEnergy(t *testing.T) { + + v := &ArrayEnergy{} + + rawData, err := json.Marshal(v) + if err != nil { + t.Fatal(err) + } + + id, _ := v.CANId() + p, err := FromJson(id, rawData) + if err != nil { + t.Fatal(err) + } + + switch underlying := p.(type) { + case *ArrayEnergy: + break + default: + t.Fatalf("didn't match type: %T, %v", underlying, underlying) + } + + +} +func TestMarshalUnmarshalArrayEnergyReset(t *testing.T) { + v := &ArrayEnergyReset{} + bin, err := v.MarshalPacket() + if err != nil { + t.Fatal(err) + } + err = v.UnmarshalPacket(bin) + if err != nil { + t.Fatal(err) + } +} + +func TestJSONArrayEnergyReset(t *testing.T) { + + v := &ArrayEnergyReset{} + + rawData, err := json.Marshal(v) + if err != nil { + t.Fatal(err) + } + + id, _ := v.CANId() + p, err := FromJson(id, rawData) + if err != nil { + t.Fatal(err) + } + + switch underlying := p.(type) { + case *ArrayEnergyReset: + break + default: + t.Fatalf("didn't match type: %T, %v", underlying, underlying) + } + + } func TestMarshalUnmarshalVisionTurnSignalsCommand(t *testing.T) { v := &VisionTurnSignalsCommand{} @@ -1663,8 +1807,8 @@ func TestJSONTrackerData(t *testing.T) { } -func TestMarshalUnmarshalTritiumMotorDrive(t *testing.T) { - v := &TritiumMotorDrive{} +func TestMarshalUnmarshalTritiumMotorDriveL(t *testing.T) { + v := &TritiumMotorDriveL{} bin, err := v.MarshalPacket() if err != nil { t.Fatal(err) @@ -1675,9 +1819,9 @@ func TestMarshalUnmarshalTritiumMotorDrive(t *testing.T) { } } -func TestJSONTritiumMotorDrive(t *testing.T) { +func TestJSONTritiumMotorDriveL(t *testing.T) { - v := &TritiumMotorDrive{} + v := &TritiumMotorDriveL{} rawData, err := json.Marshal(v) if err != nil { @@ -1691,7 +1835,7 @@ func TestJSONTritiumMotorDrive(t *testing.T) { } switch underlying := p.(type) { - case *TritiumMotorDrive: + case *TritiumMotorDriveL: break default: t.Fatalf("didn't match type: %T, %v", underlying, underlying) @@ -1699,8 +1843,8 @@ func TestJSONTritiumMotorDrive(t *testing.T) { } -func TestMarshalUnmarshalTritiumMotorPower(t *testing.T) { - v := &TritiumMotorPower{} +func TestMarshalUnmarshalTritiumMotorPowerL(t *testing.T) { + v := &TritiumMotorPowerL{} bin, err := v.MarshalPacket() if err != nil { t.Fatal(err) @@ -1711,9 +1855,9 @@ func TestMarshalUnmarshalTritiumMotorPower(t *testing.T) { } } -func TestJSONTritiumMotorPower(t *testing.T) { +func TestJSONTritiumMotorPowerL(t *testing.T) { - v := &TritiumMotorPower{} + v := &TritiumMotorPowerL{} rawData, err := json.Marshal(v) if err != nil { @@ -1727,7 +1871,7 @@ func TestJSONTritiumMotorPower(t *testing.T) { } switch underlying := p.(type) { - case *TritiumMotorPower: + case *TritiumMotorPowerL: break default: t.Fatalf("didn't match type: %T, %v", underlying, underlying) @@ -1735,8 +1879,8 @@ func TestJSONTritiumMotorPower(t *testing.T) { } -func TestMarshalUnmarshalTritiumReset(t *testing.T) { - v := &TritiumReset{} +func TestMarshalUnmarshalTritiumResetL(t *testing.T) { + v := &TritiumResetL{} bin, err := v.MarshalPacket() if err != nil { t.Fatal(err) @@ -1747,9 +1891,9 @@ func TestMarshalUnmarshalTritiumReset(t *testing.T) { } } -func TestJSONTritiumReset(t *testing.T) { +func TestJSONTritiumResetL(t *testing.T) { - v := &TritiumReset{} + v := &TritiumResetL{} rawData, err := json.Marshal(v) if err != nil { @@ -1763,7 +1907,115 @@ func TestJSONTritiumReset(t *testing.T) { } switch underlying := p.(type) { - case *TritiumReset: + case *TritiumResetL: + break + default: + t.Fatalf("didn't match type: %T, %v", underlying, underlying) + } + + +} +func TestMarshalUnmarshalTritiumMotorDriveR(t *testing.T) { + v := &TritiumMotorDriveR{} + bin, err := v.MarshalPacket() + if err != nil { + t.Fatal(err) + } + err = v.UnmarshalPacket(bin) + if err != nil { + t.Fatal(err) + } +} + +func TestJSONTritiumMotorDriveR(t *testing.T) { + + v := &TritiumMotorDriveR{} + + rawData, err := json.Marshal(v) + if err != nil { + t.Fatal(err) + } + + id, _ := v.CANId() + p, err := FromJson(id, rawData) + if err != nil { + t.Fatal(err) + } + + switch underlying := p.(type) { + case *TritiumMotorDriveR: + break + default: + t.Fatalf("didn't match type: %T, %v", underlying, underlying) + } + + +} +func TestMarshalUnmarshalTritiumMotorPowerR(t *testing.T) { + v := &TritiumMotorPowerR{} + bin, err := v.MarshalPacket() + if err != nil { + t.Fatal(err) + } + err = v.UnmarshalPacket(bin) + if err != nil { + t.Fatal(err) + } +} + +func TestJSONTritiumMotorPowerR(t *testing.T) { + + v := &TritiumMotorPowerR{} + + rawData, err := json.Marshal(v) + if err != nil { + t.Fatal(err) + } + + id, _ := v.CANId() + p, err := FromJson(id, rawData) + if err != nil { + t.Fatal(err) + } + + switch underlying := p.(type) { + case *TritiumMotorPowerR: + break + default: + t.Fatalf("didn't match type: %T, %v", underlying, underlying) + } + + +} +func TestMarshalUnmarshalTritiumResetR(t *testing.T) { + v := &TritiumResetR{} + bin, err := v.MarshalPacket() + if err != nil { + t.Fatal(err) + } + err = v.UnmarshalPacket(bin) + if err != nil { + t.Fatal(err) + } +} + +func TestJSONTritiumResetR(t *testing.T) { + + v := &TritiumResetR{} + + rawData, err := json.Marshal(v) + if err != nil { + t.Fatal(err) + } + + id, _ := v.CANId() + p, err := FromJson(id, rawData) + if err != nil { + t.Fatal(err) + } + + switch underlying := p.(type) { + case *TritiumResetR: break default: t.Fatalf("didn't match type: %T, %v", underlying, underlying) diff --git a/skylab/skylab_msgp.go b/skylab/skylab_msgp.go deleted file mode 100644 index 628df20..0000000 --- a/skylab/skylab_msgp.go +++ /dev/null @@ -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"` -} diff --git a/skylab/skylab_msgp_gen.go b/skylab/skylab_msgp_gen.go deleted file mode 100644 index 1557840..0000000 --- a/skylab/skylab_msgp_gen.go +++ /dev/null @@ -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 -} diff --git a/skylab/skylab_msgp_gen_test.go b/skylab/skylab_msgp_gen_test.go deleted file mode 100644 index a28665a..0000000 --- a/skylab/skylab_msgp_gen_test.go +++ /dev/null @@ -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) - } - } -} diff --git a/skylab/templates/golang.go.tmpl b/skylab/templates/golang.go.tmpl index 5254aa1..9b7ea79 100644 --- a/skylab/templates/golang.go.tmpl +++ b/skylab/templates/golang.go.tmpl @@ -84,7 +84,9 @@ func (p *{{$structName}}) String() string { package skylab import ( + "errors" "encoding/binary" + "github.com/kschamplin/gotelem/internal/can" "encoding/json" ) @@ -96,37 +98,42 @@ const ( {{- end}} ) +var nameToIdMap = map[string]can.CanID{ + +} + // list of every packet ID. can be used for O(1) checks. -var idMap = map[uint32]bool{ +var idMap = map[can.CanID]bool{ {{ range $p := .Packets -}} {{ if $p.Repeat }} {{ range $idx := Nx (int $p.Id) $p.Repeat $p.Offset -}} - {{ $idx | printf "0x%X"}}: true, + can.CanID{ Id: {{ $idx | printf "0x%X"}}, Extended: {{$p.Extended}} }: true, {{ end }} {{- else }} - {{ $p.Id | printf "0x%X" }}: true, + can.CanID{ Id: {{ $p.Id | printf "0x%X" }}, Extended: {{$p.Extended}} }: true, {{- end}} {{- end}} } // FromCanFrame creates a Packet from a given CAN ID and data payload. // If the CAN ID is unknown, it will return an error. -func FromCanFrame(id uint32, data []byte) (Packet, error) { +func FromCanFrame(f can.Frame) (Packet, error) { + id := f.Id if !idMap[id] { - return nil, &UnknownIdError{ id } + return nil, &UnknownIdError{ id.Id } } switch id { {{- range $p := .Packets }} {{- if $p.Repeat }} - case {{ Nx (int $p.Id) $p.Repeat $p.Offset | mapf "0x%X" | strJoin ", " -}}: + case {{ $p | idToString -}}: var res = &{{camelCase $p.Name true}}{} - res.UnmarshalPacket(data) - res.Idx = id - {{$p.Id | printf "0x%X" }} + res.UnmarshalPacket(f.Data) + res.Idx = id.Id - {{$p.Id | printf "0x%X" }} return res, nil {{- else }} - case {{ $p.Id | printf "0x%X" }}: + case {{ $p | idToString }}: var res = &{{camelCase $p.Name true}}{} - res.UnmarshalPacket(data) + res.UnmarshalPacket(f.Data) return res, nil {{- end}} {{- end}} @@ -136,28 +143,18 @@ func FromCanFrame(id uint32, data []byte) (Packet, error) { } -func FromJson (id uint32, raw []byte) (Packet, error) { - if !idMap[id] { - return nil, &UnknownIdError{ id } - } - switch id { +func FromJson (name string, raw []byte) (Packet, error) { + switch name { {{- range $p := .Packets }} - {{- if $p.Repeat }} - case {{ Nx (int $p.Id) $p.Repeat $p.Offset | mapf "0x%X" | strJoin ", " -}}: - var res = &{{camelCase $p.Name true}}{} - err := json.Unmarshal(raw, res) - res.Idx = id - {{ $p.Id | printf "0x%X" }} - return res, err - {{- else }} - case {{ $p.Id | printf "0x%X" }}: + case "{{ $p.Name }}": var res = &{{camelCase $p.Name true}}{} err := json.Unmarshal(raw, res) return res, err - {{- end }} {{- end }} } - panic("This should never happen. CAN ID didn't match but was in ID map") + return nil, errors.New("unknown packet name") + } {{range .Packets -}} diff --git a/socketcan/socketcan.go b/socketcan/socketcan.go index 0797b1e..1a5a032 100644 --- a/socketcan/socketcan.go +++ b/socketcan/socketcan.go @@ -11,7 +11,7 @@ import ( "fmt" "net" - "github.com/kschamplin/gotelem" + "github.com/kschamplin/gotelem/internal/can" "golang.org/x/sys/unix" ) @@ -128,21 +128,22 @@ func (sck *CanSocket) SetFilters(filters []CanFilter) error { } // Send sends a CAN frame -func (sck *CanSocket) Send(msg *gotelem.Frame) error { +func (sck *CanSocket) Send(msg *can.Frame) error { buf := make([]byte, fdFrameSize) - idToWrite := msg.Id + idToWrite := msg.Id.Id - switch msg.Kind { - case gotelem.CanSFFFrame: - idToWrite &= unix.CAN_SFF_MASK - case gotelem.CanEFFFrame: + if (msg.Id.Extended) { idToWrite &= unix.CAN_EFF_MASK idToWrite |= unix.CAN_EFF_FLAG - case gotelem.CanRTRFrame: + } + + + switch msg.Kind { + case can.CanRTRFrame: idToWrite |= unix.CAN_RTR_FLAG - case gotelem.CanErrFrame: + case can.CanErrFrame: return errors.New("you can't send error frames") default: return errors.New("unknown frame type") @@ -175,7 +176,7 @@ func (sck *CanSocket) Send(msg *gotelem.Frame) error { return nil } -func (sck *CanSocket) Recv() (*gotelem.Frame, error) { +func (sck *CanSocket) Recv() (*can.Frame, error) { // todo: support extended frames. buf := make([]byte, fdFrameSize) @@ -184,25 +185,33 @@ func (sck *CanSocket) Recv() (*gotelem.Frame, error) { return nil, err } - id := binary.LittleEndian.Uint32(buf[0:4]) + raw_id := binary.LittleEndian.Uint32(buf[0:4]) - var k gotelem.Kind - if id&unix.CAN_EFF_FLAG != 0 { + var id can.CanID + id.Id = raw_id + if raw_id&unix.CAN_EFF_FLAG != 0 { // extended id frame - k = gotelem.CanEFFFrame + id.Extended = true; } else { // it's a normal can frame - k = gotelem.CanSFFFrame + id.Extended = false; } - if id&unix.CAN_ERR_FLAG != 0 { + var k can.Kind = can.CanDataFrame + + if raw_id&unix.CAN_ERR_FLAG != 0 { // we got an error... + k = can.CanErrFrame + } + + if raw_id & unix.CAN_RTR_FLAG != 0 { + k = can.CanRTRFrame } dataLength := uint8(buf[4]) - result := &gotelem.Frame{ - Id: id & unix.CAN_EFF_MASK, + result := &can.Frame{ + Id: id, Kind: k, Data: buf[8 : dataLength+8], }