skylab: rework interface

This commit is contained in:
saji 2023-05-28 19:39:03 -05:00
parent 96149ee38b
commit 2dceb3927e
17 changed files with 921 additions and 1357 deletions

View file

@ -2,104 +2,29 @@ package gotelem
import (
"errors"
"fmt"
"sync"
"github.com/kschamplin/gotelem/skylab"
"golang.org/x/exp/slog"
)
type BrokerRequest struct {
Source string // the name of the sender
Msg Frame // the message to send
}
type BrokerClient struct {
Name string // the name of the client
Ch chan Frame // the channel to send frames to this client
}
type Broker struct {
subs map[string]chan Frame
publishCh chan BrokerRequest
subsCh chan BrokerClient
unsubCh chan BrokerClient
}
// Start runs the broker and sends messages to the subscribers (but not the sender)
func (b *Broker) Start() {
for {
select {
case newClient := <-b.subsCh:
b.subs[newClient.Name] = newClient.Ch
case req := <-b.publishCh:
for name, ch := range b.subs {
if name == req.Source {
continue // don't send to ourselves.
}
// a kinda-inelegant non-blocking push.
// if we can't do it, we just drop it. this should ideally never happen.
select {
case ch <- req.Msg:
default:
fmt.Printf("we dropped a packet to dest %s", name)
}
}
case clientToRemove := <-b.unsubCh:
close(b.subs[clientToRemove.Name])
delete(b.subs, clientToRemove.Name)
}
}
}
func (b *Broker) Publish(name string, msg Frame) {
breq := BrokerRequest{
Source: name,
Msg: msg,
}
b.publishCh <- breq
}
func (b *Broker) Subscribe(name string) <-chan Frame {
ch := make(chan Frame, 3)
bc := BrokerClient{
Name: name,
Ch: ch,
}
b.subsCh <- bc
return ch
}
func (b *Broker) Unsubscribe(name string) {
bc := BrokerClient{
Name: name,
}
b.unsubCh <- bc
}
type JBroker struct {
subs map[string] chan CANDumpEntry // contains the channel for each subsciber
subs map[string]chan skylab.BusEvent // contains the channel for each subsciber
logger *slog.Logger
lock sync.RWMutex
logger *slog.Logger
lock sync.RWMutex
bufsize int // size of chan buffer in elements.
}
func NewBroker(bufsize int, logger *slog.Logger) *JBroker {
return &JBroker{
subs: make(map[string]chan CANDumpEntry),
logger: logger,
subs: make(map[string]chan skylab.BusEvent),
logger: logger,
bufsize: bufsize,
}
}
func (b *JBroker) Subscribe(name string) (ch chan CANDumpEntry, err error) {
func (b *JBroker) Subscribe(name string) (ch chan skylab.BusEvent, err error) {
// get rw lock.
b.lock.Lock()
defer b.lock.Unlock()
@ -108,7 +33,7 @@ func (b *JBroker) Subscribe(name string) (ch chan CANDumpEntry, err error) {
return nil, errors.New("name already in use")
}
b.logger.Info("new subscriber", "name", name)
ch = make(chan CANDumpEntry, b.bufsize)
ch = make(chan skylab.BusEvent, b.bufsize)
b.subs[name] = ch
return
@ -121,7 +46,7 @@ func (b *JBroker) Unsubscribe(name string) {
delete(b.subs, name)
}
func (b *JBroker) Publish(sender string, message CANDumpEntry) {
func (b *JBroker) Publish(sender string, message skylab.BusEvent) {
b.lock.RLock()
defer b.lock.RUnlock()
for name, ch := range b.subs {

View file

@ -3,28 +3,26 @@ package cli
import (
"fmt"
imgui "github.com/AllenDang/cimgui-go"
"github.com/kschamplin/gotelem"
"github.com/kschamplin/gotelem/mprpc"
"github.com/urfave/cli/v2"
imgui "github.com/AllenDang/cimgui-go"
)
func init() {
subCmds = append(subCmds, clientCmd)
}
var clientCmd = &cli.Command{
Name: "client",
Aliases: []string{"c"},
Usage: "interact with a gotelem server",
Name: "client",
Aliases: []string{"c"},
Usage: "interact with a gotelem server",
ArgsUsage: "[server url]",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "gui",
Name: "gui",
Aliases: []string{"g"},
Usage: "start a local TUI",
Usage: "start a local TUI",
},
},
Description: `
@ -33,9 +31,8 @@ Connects to a gotelem server or relay. Can be used to
Action: client,
}
func loop() {
imgui.ShowDemoWindow()
imgui.ShowDemoWindow()
}
func client(ctx *cli.Context) error {
@ -45,13 +42,8 @@ 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){
func CANFrameHandler(f *gotelem.Frame) (*mprpc.RPCEmpty, error) {
fmt.Printf("got frame, %v\n", f)
return nil, nil
}
var initialRPCHandlers = map[string]mprpc.ServiceFunc{
"can": mprpc.MakeService(CANFrameHandler),
}

View file

@ -37,11 +37,9 @@ var serveCmd = &cli.Command{
Action: serve,
}
// FIXME: naming
// this is a server handler for i.e tcp socket, http server, socketCAN, xbee,
// etc. we can register them in init() functions.
type testThing func(cCtx *cli.Context, broker *gotelem.Broker, logger *slog.Logger) (err error)
type service interface {
fmt.Stringer
@ -58,12 +56,6 @@ var serveThings = []service{
&rpcService{},
}
func deriveLogger (oldLogger *slog.Logger, svc service) (newLogger *slog.Logger) {
newLogger = oldLogger.With("svc", svc.String())
return
}
func serve(cCtx *cli.Context) error {
// TODO: output both to stderr and a file.
logger := slog.New(slog.NewTextHandler(os.Stderr))
@ -85,14 +77,11 @@ func serve(cCtx *cli.Context) error {
}(svc, logger)
}
wg.Wait()
return nil
}
type rpcService struct {
}
@ -162,8 +151,7 @@ func (c *CanLoggerService) String() string {
func (c *CanLoggerService) Status() {
}
func (c *CanLoggerService) Start(cCtx *cli.Context, broker *gotelem.JBroker, l *slog.Logger) (err error) {
func (c *CanLoggerService) Start(cCtx *cli.Context, broker *gotelem.JBroker, l *slog.Logger) (err error) {
rxCh, err := broker.Subscribe("canDump")
if err != nil {
return err
@ -171,7 +159,7 @@ func (c *CanLoggerService) Start(cCtx *cli.Context, broker *gotelem.JBroker, l
t := time.Now()
fname := fmt.Sprintf("candump_%d-%02d-%02dT%02d.%02d.%02d.txt",
t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
l.Info("logging to file", "filename", fname)
f, err := os.Create(fname)
@ -194,7 +182,6 @@ func (c *CanLoggerService) Start(cCtx *cli.Context, broker *gotelem.JBroker, l
}
}
// XBeeService provides data over an Xbee device, either by serial or TCP
// based on the url provided in the xbee flag. see the description for details.
type XBeeService struct {
@ -207,7 +194,6 @@ func (x *XBeeService) String() string {
func (x *XBeeService) Status() {
}
func (x *XBeeService) Start(cCtx *cli.Context, broker *gotelem.JBroker, logger *slog.Logger) (err error) {
if cCtx.String("xbee") == "" {
logger.Info("not using xbee")
@ -245,7 +231,5 @@ func (x *XBeeService) Start(cCtx *cli.Context, broker *gotelem.JBroker, logger *
}
}
}
}

View file

@ -17,10 +17,10 @@ import (
// It is an example of the modular architecture of the command line and server stack.
var canDevFlag = &cli.StringFlag{
Name: "can",
Aliases: []string{"c"},
Usage: "CAN device string",
EnvVars: []string{"CAN_DEVICE"},
Name: "can",
Aliases: []string{"c"},
Usage: "CAN device string",
EnvVars: []string{"CAN_DEVICE"},
}
// this function sets up the `serve` flags and services that use socketCAN
@ -37,10 +37,9 @@ func init() {
subCmds = append(subCmds, socketCANCmd)
}
type socketCANService struct {
name string
sock socketcan.CanSocket
sock *socketcan.CanSocket
}
func (s *socketCANService) Status() {
@ -66,13 +65,13 @@ func (s *socketCANService) Start(cCtx *cli.Context, broker *gotelem.JBroker, log
go vcanTest(cCtx.String("can"))
}
sock, err := socketcan.NewCanSocket(cCtx.String("can"))
s.sock, err = socketcan.NewCanSocket(cCtx.String("can"))
if err != nil {
logger.Error("error opening socket", "err", err)
return
}
defer sock.Close()
s.name = sock.Name()
defer s.sock.Close()
s.name = s.sock.Name()
// connect to the broker
rxCh, err := broker.Subscribe("socketCAN")
@ -81,13 +80,12 @@ func (s *socketCANService) Start(cCtx *cli.Context, broker *gotelem.JBroker, log
}
defer broker.Unsubscribe("socketCAN")
// make a channel to receive socketCAN frames.
rxCan := make(chan gotelem.Frame)
go func() {
for {
pkt, err := sock.Recv()
pkt, err := s.sock.Recv()
if err != nil {
logger.Warn("error receiving CAN packet", "err", err)
}
@ -99,13 +97,13 @@ func (s *socketCANService) Start(cCtx *cli.Context, broker *gotelem.JBroker, log
for {
select {
case msg := <-rxCh:
id, d, _ := skylab.CanSend(msg.Data)
id, d, _ := skylab.ToCanFrame(msg.Data)
frame.Id = id
frame.Data = d
sock.Send(&frame)
s.sock.Send(&frame)
case msg := <-rxCan:
p, err := skylab.FromCanFrame(msg.Id, msg.Data)
@ -113,10 +111,10 @@ func (s *socketCANService) Start(cCtx *cli.Context, broker *gotelem.JBroker, log
logger.Warn("error parsing can packet", "id", msg.Id)
continue
}
cde := gotelem.CANDumpEntry{
cde := skylab.BusEvent{
Timestamp: float64(time.Now().UnixNano()) / 1e9,
Id: uint64(msg.Id),
Data: p,
Id: uint64(msg.Id),
Data: p,
}
broker.Publish("socketCAN", cde)
case <-cCtx.Done():
@ -161,14 +159,13 @@ func vcanTest(devname string) {
Id: 0.2,
}
id, data, err := skylab.CanSend(&testPkt)
id, data, err := skylab.ToCanFrame(&testPkt)
testFrame := gotelem.Frame{
Id: id,
Id: id,
Data: data,
Kind: gotelem.CanSFFFrame,
}
for {
slog.Info("sending test packet")
sock.Send(&testFrame)

View file

@ -12,7 +12,6 @@ import (
"strings"
"syscall"
"github.com/kschamplin/gotelem"
"github.com/kschamplin/gotelem/skylab"
"github.com/urfave/cli/v2"
)
@ -92,7 +91,7 @@ func run(ctx *cli.Context) (err error) {
segments := strings.Split(dumpLine, " ")
var cd gotelem.CANDumpEntry
var cd skylab.BusEvent
// this is cursed but easiest way to get a float from a string.
fmt.Sscanf(segments[0], "(%g)", &cd.Timestamp)

View file

@ -6,7 +6,6 @@
// by writing "adapters" to various devices/formats (xbee, sqlite, network socket, socketcan)
package gotelem
// 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 {
@ -15,7 +14,6 @@ type Frame struct {
Kind Kind
}
//go:generate msgp
type CANFrame interface {
Id() uint32
Data() []byte
@ -58,4 +56,3 @@ type CanTransciever interface {
CanSink
CanSource
}

1
go.mod
View file

@ -20,4 +20,5 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/stretchr/testify v1.8.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View file

@ -18,14 +18,13 @@ import (
// SkylabFile is a yaml file from skylab.
type SkylabFile struct {
Packets []PacketDef
Boards []BoardSpec
Boards []BoardSpec
}
type BoardSpec struct {
Name string
Name string
Transmit []string
Recieve []string
Recieve []string
}
// data field.
@ -80,8 +79,12 @@ var typeSizeMap = map[string]uint{
"bitfield": 1,
}
func MapType(ctype string) string {
return typeMap[ctype]
}
func (d *DataField) ToStructMember(parentName string) string {
if d.Type == "bitfield" {
bfStructName := parentName + toCamelInitCase(d.Name, true)
return toCamelInitCase(d.Name, true) + " " + bfStructName
@ -96,32 +99,31 @@ func (d *DataField) MakeMarshal(offset int) string {
if d.Type == "uint8_t" || d.Type == "int8_t" {
return fmt.Sprintf("b[%d] = p.%s", offset, fieldName)
} else if d.Type == "bitfield" {
return fmt.Sprintf("b[%d] = p.%s.Marshal()", offset,fieldName)
return fmt.Sprintf("b[%d] = p.%s.MarshalByte()", offset, fieldName)
} else if d.Type == "float" {
return fmt.Sprintf("float32ToBytes(b[%d:], p.%s, false)", offset, fieldName)
} else if t ,ok := typeMap[d.Type]; ok {
} else if t, ok := typeMap[d.Type]; ok {
// it's uint or int of some kind, use endian to write it.
if strings.HasPrefix(t, "i") {
// this means it's a signed integer.
// encoding/binary does not support putting signed ints, instead
// we should cast it to unsigned and then use the unsigned int functions.
return fmt.Sprintf("binary.LittleEndian.PutU%s(b[%d:], u%s(p.%s))", t, offset, t, fieldName)
}
}
return fmt.Sprintf("binary.LittleEndian.Put%s(b[%d:], p.%s)", toCamelInitCase(t, true), offset, fieldName)
}
return "panic(\"failed to do it\")\n"
}
func (d *DataField) MakeUnmarshal(offset int) string {
fieldName := toCamelInitCase(d.Name, true)
if d.Type == "uint8_t" || d.Type == "int8_t" {
return fmt.Sprintf("p.%s = b[%d]", fieldName, offset)
} else if d.Type == "bitfield" {
return fmt.Sprintf("p.%s.Unmarshal(b[%d])", fieldName, offset)
return fmt.Sprintf("p.%s.UnmarshalByte(b[%d])", fieldName, offset)
} else if d.Type == "float" {
return fmt.Sprintf("p.%s = float32FromBytes(b[%d:], false)", fieldName, offset)
@ -134,14 +136,12 @@ func (d *DataField) MakeUnmarshal(offset int) string {
// encoding/binary does not support putting signed ints, instead
// we should cast it to unsigned and then use the unsigned int functions.
return fmt.Sprintf("p.%s = %s(binary.LittleEndian.U%s(b[%d:]))", fieldName, t, t, offset)
}
}
return fmt.Sprintf("p.%s = binary.LittleEndian.%s(b[%d:])", fieldName, toCamelInitCase(t, true), offset)
}
panic("unhandled type")
}
func (p PacketDef) CalcSize() int {
// makes a function that returns the size of the code.
@ -153,7 +153,6 @@ func (p PacketDef) CalcSize() int {
return size
}
func (p PacketDef) MakeMarshal() string {
var buf strings.Builder
@ -170,14 +169,12 @@ func (p PacketDef) MakeMarshal() string {
offset += int(typeSizeMap[val.Type])
}
return buf.String()
}
func (p PacketDef) MakeUnmarshal() string {
var buf strings.Builder
var offset int = 0
for _, val := range p.Data {
@ -190,7 +187,6 @@ func (p PacketDef) MakeUnmarshal() string {
return buf.String()
}
// stolen camelCaser code. initCase = true means CamelCase, false means camelCase
func toCamelInitCase(s string, initCase bool) string {
s = strings.TrimSpace(s)
@ -228,29 +224,28 @@ func toCamelInitCase(s string, initCase bool) string {
return n.String()
}
// N takes a start and stop value and returns a stream of
// N takes a start and stop value and returns a stream of
// [start, end), including the starting value but excluding the end value.
func N(start, end int) (stream chan int) {
stream = make(chan int)
go func() {
for i := start; i < end; i++ {
stream <- i
}
close(stream)
}()
return
stream = make(chan int)
go func() {
for i := start; i < end; i++ {
stream <- i
}
close(stream)
}()
return
}
// Nx takes a start, a quantity, and an offset and returns a stream
// of `times` values which count from start and increment by `offset` each
// time.
func Nx (start, times, offset int) (elems []int) {
func Nx(start, times, offset int) (elems []int) {
elems = make([]int, times)
for i := 0; i < times; i++ {
elems[i] = start + offset * i
elems[i] = start + offset*i
}
return
return
}
// dumb function for type conversion between uint32 to integer
@ -259,10 +254,10 @@ func uint32ToInt(i uint32) (o int) {
return int(i)
}
// strJoin is a remapping of strings.Join so that we can use
// it in a pipeline.
// {{.Names | strJoin ", " }}
//
// {{.Names | strJoin ", " }}
func strJoin(delim string, elems []string) string {
return strings.Join(elems, delim)
}
@ -277,7 +272,6 @@ func mapf(format string, els []int) []string {
return resp
}
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.
@ -312,15 +306,16 @@ func main() {
v.Boards = append(v.Boards, newFile.Boards...)
}
// we add any functions mapping we need here.
// 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,
"Time": time.Now,
"N": N,
"Nx": Nx,
"int": uint32ToInt,
"strJoin": strJoin,
"mapf": mapf,
"maptype": MapType,
}
tmpl, err := template.New("golang.go.tmpl").Funcs(fnMap).ParseGlob("templates/*.go.tmpl")
@ -336,7 +331,6 @@ func main() {
}
err = tmpl.Execute(f, v)
if err != nil {
panic(err)
}
@ -354,4 +348,3 @@ func main() {
tests.Execute(testF, v)
}

View file

@ -61,7 +61,7 @@ type Sizer interface {
}
// CanSend takes a packet and makes CAN framing data.
func CanSend(p Packet) (id uint32, data []byte, err error) {
func ToCanFrame(p Packet) (id uint32, data []byte, err error) {
id, err = p.CANId()
if err != nil {
@ -71,29 +71,86 @@ func CanSend(p Packet) (id uint32, data []byte, err error) {
return
}
// ---- JSON encoding business ----
// ---- other wire encoding business ----
type JSONPacket struct {
Id uint32
Data json.RawMessage
// internal structure for partially decoding json object.
type jsonRawEvent struct {
Timestamp float64
Id uint32
Name string
Data json.RawMessage
}
func ToJson(p Packet) (*JSONPacket, error) {
d, err := json.Marshal(p)
// BusEvent is a timestamped Skylab packet designed to be serialized.
type BusEvent struct {
Timestamp float64 `json:"ts"`
Id uint64 `json:"id"`
Name string `json:"name"`
Data Packet `json:"data"`
}
// FIXME: handle Name field.
func (e *BusEvent) MarshalJSON() (b []byte, err error) {
// create the underlying raw event
j := &jsonRawEvent{
Timestamp: e.Timestamp,
Id: uint32(e.Id),
}
// now we use the magic Packet -> map[string]interface{} function
j.Data, err = json.Marshal(e.Data)
if err != nil {
return nil, err
}
id, err := p.CANId()
return json.Marshal(j)
}
func (e *BusEvent) UnmarshalJSON(b []byte) error {
var jRaw *jsonRawEvent
err := json.Unmarshal(b, jRaw)
if err != nil {
return err
}
e.Timestamp = jRaw.Timestamp
e.Id = uint64(jRaw.Id)
e.Data, err = FromJson(jRaw.Id, jRaw.Data)
return err
}
// FIXME: handle name field.
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: e.Timestamp,
Id: uint32(e.Id),
Data: data,
}
jp := &JSONPacket{Id: id, Data: d}
return rawEv.MarshalMsg(b)
}
return jp, nil
func (e *BusEvent) UnmarshalMsg(b []byte) ([]byte, error) {
rawEv := &msgpRawEvent{}
remain, err := rawEv.UnmarshalMsg(b)
if err != nil {
return remain, err
}
e.Timestamp = rawEv.Timestamp
e.Id = uint64(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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

18
skylab/skylab_msgp.go Normal file
View file

@ -0,0 +1,18 @@
package skylab
//go:generate msgp -unexported
// internal structure for handling
type msgpRawEvent struct {
Timestamp float64 `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"`
}

View file

@ -1,4 +1,4 @@
package gotelem
package skylab
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
@ -7,7 +7,7 @@ import (
)
// DecodeMsg implements msgp.Decodable
func (z *CanFilter) DecodeMsg(dc *msgp.Reader) (err error) {
func (z *msgpRawEvent) DecodeMsg(dc *msgp.Reader) (err error) {
var field []byte
_ = field
var zb0001 uint32
@ -24,181 +24,24 @@ func (z *CanFilter) DecodeMsg(dc *msgp.Reader) (err error) {
return
}
switch msgp.UnsafeString(field) {
case "Id":
case "ts":
z.Timestamp, err = dc.ReadFloat64()
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 "Mask":
z.Mask, err = dc.ReadUint32()
if err != nil {
err = msgp.WrapError(err, "Mask")
return
}
case "Inverted":
z.Inverted, err = dc.ReadBool()
if err != nil {
err = msgp.WrapError(err, "Inverted")
return
}
default:
err = dc.Skip()
if err != nil {
err = msgp.WrapError(err)
return
}
}
}
return
}
// EncodeMsg implements msgp.Encodable
func (z CanFilter) EncodeMsg(en *msgp.Writer) (err error) {
// map header, size 3
// write "Id"
err = en.Append(0x83, 0xa2, 0x49, 0x64)
if err != nil {
return
}
err = en.WriteUint32(z.Id)
if err != nil {
err = msgp.WrapError(err, "Id")
return
}
// write "Mask"
err = en.Append(0xa4, 0x4d, 0x61, 0x73, 0x6b)
if err != nil {
return
}
err = en.WriteUint32(z.Mask)
if err != nil {
err = msgp.WrapError(err, "Mask")
return
}
// write "Inverted"
err = en.Append(0xa8, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64)
if err != nil {
return
}
err = en.WriteBool(z.Inverted)
if err != nil {
err = msgp.WrapError(err, "Inverted")
return
}
return
}
// MarshalMsg implements msgp.Marshaler
func (z CanFilter) MarshalMsg(b []byte) (o []byte, err error) {
o = msgp.Require(b, z.Msgsize())
// map header, size 3
// string "Id"
o = append(o, 0x83, 0xa2, 0x49, 0x64)
o = msgp.AppendUint32(o, z.Id)
// string "Mask"
o = append(o, 0xa4, 0x4d, 0x61, 0x73, 0x6b)
o = msgp.AppendUint32(o, z.Mask)
// string "Inverted"
o = append(o, 0xa8, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64)
o = msgp.AppendBool(o, z.Inverted)
return
}
// UnmarshalMsg implements msgp.Unmarshaler
func (z *CanFilter) 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 "Mask":
z.Mask, bts, err = msgp.ReadUint32Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "Mask")
return
}
case "Inverted":
z.Inverted, bts, err = msgp.ReadBoolBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Inverted")
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 CanFilter) Msgsize() (s int) {
s = 1 + 3 + msgp.Uint32Size + 5 + msgp.Uint32Size + 9 + msgp.BoolSize
return
}
// DecodeMsg implements msgp.Decodable
func (z *Frame) 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":
case "data":
z.Data, err = dc.ReadBytes(z.Data)
if err != nil {
err = msgp.WrapError(err, "Data")
return
}
case "Kind":
{
var zb0002 uint8
zb0002, err = dc.ReadUint8()
if err != nil {
err = msgp.WrapError(err, "Kind")
return
}
z.Kind = Kind(zb0002)
}
default:
err = dc.Skip()
if err != nil {
@ -211,10 +54,20 @@ func (z *Frame) DecodeMsg(dc *msgp.Reader) (err error) {
}
// EncodeMsg implements msgp.Encodable
func (z *Frame) EncodeMsg(en *msgp.Writer) (err error) {
func (z *msgpRawEvent) EncodeMsg(en *msgp.Writer) (err error) {
// map header, size 3
// write "Id"
err = en.Append(0x83, 0xa2, 0x49, 0x64)
// write "ts"
err = en.Append(0x83, 0xa2, 0x74, 0x73)
if err != nil {
return
}
err = en.WriteFloat64(z.Timestamp)
if err != nil {
err = msgp.WrapError(err, "Timestamp")
return
}
// write "id"
err = en.Append(0xa2, 0x69, 0x64)
if err != nil {
return
}
@ -223,8 +76,8 @@ func (z *Frame) EncodeMsg(en *msgp.Writer) (err error) {
err = msgp.WrapError(err, "Id")
return
}
// write "Data"
err = en.Append(0xa4, 0x44, 0x61, 0x74, 0x61)
// write "data"
err = en.Append(0xa4, 0x64, 0x61, 0x74, 0x61)
if err != nil {
return
}
@ -233,37 +86,27 @@ func (z *Frame) EncodeMsg(en *msgp.Writer) (err error) {
err = msgp.WrapError(err, "Data")
return
}
// write "Kind"
err = en.Append(0xa4, 0x4b, 0x69, 0x6e, 0x64)
if err != nil {
return
}
err = en.WriteUint8(uint8(z.Kind))
if err != nil {
err = msgp.WrapError(err, "Kind")
return
}
return
}
// MarshalMsg implements msgp.Marshaler
func (z *Frame) MarshalMsg(b []byte) (o []byte, err error) {
func (z *msgpRawEvent) MarshalMsg(b []byte) (o []byte, err error) {
o = msgp.Require(b, z.Msgsize())
// map header, size 3
// string "Id"
o = append(o, 0x83, 0xa2, 0x49, 0x64)
// string "ts"
o = append(o, 0x83, 0xa2, 0x74, 0x73)
o = msgp.AppendFloat64(o, z.Timestamp)
// string "id"
o = append(o, 0xa2, 0x69, 0x64)
o = msgp.AppendUint32(o, z.Id)
// string "Data"
o = append(o, 0xa4, 0x44, 0x61, 0x74, 0x61)
// string "data"
o = append(o, 0xa4, 0x64, 0x61, 0x74, 0x61)
o = msgp.AppendBytes(o, z.Data)
// string "Kind"
o = append(o, 0xa4, 0x4b, 0x69, 0x6e, 0x64)
o = msgp.AppendUint8(o, uint8(z.Kind))
return
}
// UnmarshalMsg implements msgp.Unmarshaler
func (z *Frame) UnmarshalMsg(bts []byte) (o []byte, err error) {
func (z *msgpRawEvent) UnmarshalMsg(bts []byte) (o []byte, err error) {
var field []byte
_ = field
var zb0001 uint32
@ -280,28 +123,24 @@ func (z *Frame) UnmarshalMsg(bts []byte) (o []byte, err error) {
return
}
switch msgp.UnsafeString(field) {
case "Id":
case "ts":
z.Timestamp, bts, err = msgp.ReadFloat64Bytes(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":
case "data":
z.Data, bts, err = msgp.ReadBytesBytes(bts, z.Data)
if err != nil {
err = msgp.WrapError(err, "Data")
return
}
case "Kind":
{
var zb0002 uint8
zb0002, bts, err = msgp.ReadUint8Bytes(bts)
if err != nil {
err = msgp.WrapError(err, "Kind")
return
}
z.Kind = Kind(zb0002)
}
default:
bts, err = msgp.Skip(bts)
if err != nil {
@ -315,59 +154,135 @@ func (z *Frame) UnmarshalMsg(bts []byte) (o []byte, err error) {
}
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
func (z *Frame) Msgsize() (s int) {
s = 1 + 3 + msgp.Uint32Size + 5 + msgp.BytesPrefixSize + len(z.Data) + 5 + msgp.Uint8Size
func (z *msgpRawEvent) Msgsize() (s int) {
s = 1 + 3 + msgp.Float64Size + 3 + msgp.Uint32Size + 5 + msgp.BytesPrefixSize + len(z.Data)
return
}
// DecodeMsg implements msgp.Decodable
func (z *Kind) DecodeMsg(dc *msgp.Reader) (err error) {
{
var zb0001 uint8
zb0001, err = dc.ReadUint8()
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
}
(*z) = Kind(zb0001)
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 Kind) EncodeMsg(en *msgp.Writer) (err error) {
err = en.WriteUint8(uint8(z))
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 {
err = msgp.WrapError(err)
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 Kind) MarshalMsg(b []byte) (o []byte, err error) {
func (z *msgpRawPacket) MarshalMsg(b []byte) (o []byte, err error) {
o = msgp.Require(b, z.Msgsize())
o = msgp.AppendUint8(o, uint8(z))
// 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 *Kind) UnmarshalMsg(bts []byte) (o []byte, err error) {
{
var zb0001 uint8
zb0001, bts, err = msgp.ReadUint8Bytes(bts)
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
}
(*z) = Kind(zb0001)
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 Kind) Msgsize() (s int) {
s = msgp.Uint8Size
func (z *msgpRawPacket) Msgsize() (s int) {
s = 1 + 3 + msgp.Uint32Size + 5 + msgp.BytesPrefixSize + len(z.Data)
return
}

View file

@ -1,4 +1,4 @@
package gotelem
package skylab
// Code generated by github.com/tinylib/msgp DO NOT EDIT.
@ -9,8 +9,8 @@ import (
"github.com/tinylib/msgp/msgp"
)
func TestMarshalUnmarshalCanFilter(t *testing.T) {
v := CanFilter{}
func TestMarshalUnmarshalmsgpRawEvent(t *testing.T) {
v := msgpRawEvent{}
bts, err := v.MarshalMsg(nil)
if err != nil {
t.Fatal(err)
@ -32,8 +32,8 @@ func TestMarshalUnmarshalCanFilter(t *testing.T) {
}
}
func BenchmarkMarshalMsgCanFilter(b *testing.B) {
v := CanFilter{}
func BenchmarkMarshalMsgmsgpRawEvent(b *testing.B) {
v := msgpRawEvent{}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
@ -41,8 +41,8 @@ func BenchmarkMarshalMsgCanFilter(b *testing.B) {
}
}
func BenchmarkAppendMsgCanFilter(b *testing.B) {
v := CanFilter{}
func BenchmarkAppendMsgmsgpRawEvent(b *testing.B) {
v := msgpRawEvent{}
bts := make([]byte, 0, v.Msgsize())
bts, _ = v.MarshalMsg(bts[0:0])
b.SetBytes(int64(len(bts)))
@ -53,8 +53,8 @@ func BenchmarkAppendMsgCanFilter(b *testing.B) {
}
}
func BenchmarkUnmarshalCanFilter(b *testing.B) {
v := CanFilter{}
func BenchmarkUnmarshalmsgpRawEvent(b *testing.B) {
v := msgpRawEvent{}
bts, _ := v.MarshalMsg(nil)
b.ReportAllocs()
b.SetBytes(int64(len(bts)))
@ -67,17 +67,17 @@ func BenchmarkUnmarshalCanFilter(b *testing.B) {
}
}
func TestEncodeDecodeCanFilter(t *testing.T) {
v := CanFilter{}
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: TestEncodeDecodeCanFilter Msgsize() is inaccurate")
t.Log("WARNING: TestEncodeDecodemsgpRawEvent Msgsize() is inaccurate")
}
vn := CanFilter{}
vn := msgpRawEvent{}
err := msgp.Decode(&buf, &vn)
if err != nil {
t.Error(err)
@ -91,8 +91,8 @@ func TestEncodeDecodeCanFilter(t *testing.T) {
}
}
func BenchmarkEncodeCanFilter(b *testing.B) {
v := CanFilter{}
func BenchmarkEncodemsgpRawEvent(b *testing.B) {
v := msgpRawEvent{}
var buf bytes.Buffer
msgp.Encode(&buf, &v)
b.SetBytes(int64(buf.Len()))
@ -105,8 +105,8 @@ func BenchmarkEncodeCanFilter(b *testing.B) {
en.Flush()
}
func BenchmarkDecodeCanFilter(b *testing.B) {
v := CanFilter{}
func BenchmarkDecodemsgpRawEvent(b *testing.B) {
v := msgpRawEvent{}
var buf bytes.Buffer
msgp.Encode(&buf, &v)
b.SetBytes(int64(buf.Len()))
@ -122,8 +122,8 @@ func BenchmarkDecodeCanFilter(b *testing.B) {
}
}
func TestMarshalUnmarshalFrame(t *testing.T) {
v := Frame{}
func TestMarshalUnmarshalmsgpRawPacket(t *testing.T) {
v := msgpRawPacket{}
bts, err := v.MarshalMsg(nil)
if err != nil {
t.Fatal(err)
@ -145,8 +145,8 @@ func TestMarshalUnmarshalFrame(t *testing.T) {
}
}
func BenchmarkMarshalMsgFrame(b *testing.B) {
v := Frame{}
func BenchmarkMarshalMsgmsgpRawPacket(b *testing.B) {
v := msgpRawPacket{}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
@ -154,8 +154,8 @@ func BenchmarkMarshalMsgFrame(b *testing.B) {
}
}
func BenchmarkAppendMsgFrame(b *testing.B) {
v := Frame{}
func BenchmarkAppendMsgmsgpRawPacket(b *testing.B) {
v := msgpRawPacket{}
bts := make([]byte, 0, v.Msgsize())
bts, _ = v.MarshalMsg(bts[0:0])
b.SetBytes(int64(len(bts)))
@ -166,8 +166,8 @@ func BenchmarkAppendMsgFrame(b *testing.B) {
}
}
func BenchmarkUnmarshalFrame(b *testing.B) {
v := Frame{}
func BenchmarkUnmarshalmsgpRawPacket(b *testing.B) {
v := msgpRawPacket{}
bts, _ := v.MarshalMsg(nil)
b.ReportAllocs()
b.SetBytes(int64(len(bts)))
@ -180,17 +180,17 @@ func BenchmarkUnmarshalFrame(b *testing.B) {
}
}
func TestEncodeDecodeFrame(t *testing.T) {
v := Frame{}
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: TestEncodeDecodeFrame Msgsize() is inaccurate")
t.Log("WARNING: TestEncodeDecodemsgpRawPacket Msgsize() is inaccurate")
}
vn := Frame{}
vn := msgpRawPacket{}
err := msgp.Decode(&buf, &vn)
if err != nil {
t.Error(err)
@ -204,8 +204,8 @@ func TestEncodeDecodeFrame(t *testing.T) {
}
}
func BenchmarkEncodeFrame(b *testing.B) {
v := Frame{}
func BenchmarkEncodemsgpRawPacket(b *testing.B) {
v := msgpRawPacket{}
var buf bytes.Buffer
msgp.Encode(&buf, &v)
b.SetBytes(int64(buf.Len()))
@ -218,8 +218,8 @@ func BenchmarkEncodeFrame(b *testing.B) {
en.Flush()
}
func BenchmarkDecodeFrame(b *testing.B) {
v := Frame{}
func BenchmarkDecodemsgpRawPacket(b *testing.B) {
v := msgpRawPacket{}
var buf bytes.Buffer
msgp.Encode(&buf, &v)
b.SetBytes(int64(buf.Len()))

View file

@ -11,7 +11,7 @@ type {{$bfname}} struct {
{{- end}}
}
func (p *{{$bfname}}) Marshal() byte {
func (p *{{$bfname}}) MarshalByte() byte {
var b byte
{{- range $idx, $el := .Bits}}
{{- $bitName := camelCase $el.Name true}}
@ -22,7 +22,7 @@ func (p *{{$bfname}}) Marshal() byte {
return b
}
func (p *{{$bfname}}) Unmarshal(b byte) {
func (p *{{$bfname}}) UnmarshalByte(b byte) {
{{- range $idx, $el := .Bits}}
{{- $bitName := camelCase $el.Name true }}
p.{{$bitName}} = (b & (1 << {{ $idx }})) != 0
@ -114,7 +114,7 @@ var idMap = map[uint32]bool{
// If the CAN ID is unknown, it will return an error.
func FromCanFrame(id uint32, data []byte) (Packet, error) {
if !idMap[id] {
return nil, errors.New("Unknown Id")
return nil, errors.New("unknown id")
}
switch id {
{{- range $p := .Packets }}
@ -133,34 +133,32 @@ func FromCanFrame(id uint32, data []byte) (Packet, error) {
{{- end}}
}
return nil, errors.New("failed to match Id, something is really wrong!")
return nil, errors.New("failed to match Id, something is really wrong")
}
func FromJson (raw []byte) (Packet, error) {
// attempt to parse the JSON to a JSONPacket
jp := &JSONPacket{}
err := json.Unmarshal(raw, jp)
if err != nil {
return nil, err
func FromJson (id uint32, raw []byte) (Packet, error) {
if !idMap[id] {
return nil, errors.New("unknown id")
}
switch jp.Id {
switch id {
{{- 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(jp.Data, res)
res.Idx = jp.Id - {{ $p.Id | printf "0x%X" }}
err := json.Unmarshal(raw, res)
res.Idx = id - {{ $p.Id | printf "0x%X" }}
return res, err
{{- else }}
case {{ $p.Id | printf "0x%X" }}:
var res = &{{camelCase $p.Name true}}{}
err := json.Unmarshal(jp.Data, res)
err := json.Unmarshal(raw, res)
return res, err
{{- end }}
{{- end }}
}
return nil, errors.New("aaa")
return nil, errors.New("failed to match id")
}
{{range .Packets -}}

View file

@ -23,17 +23,14 @@ func TestMarshalUnmarshal{{$structName}}(t *testing.T) {
func TestJSON{{$structName}}(t *testing.T) {
v := &{{$structName}}{}
jp, err := ToJson(v)
rawData, err := json.Marshal(v)
if err != nil {
t.Fatal(err)
}
rawData, err := json.Marshal(jp)
if err != nil {
t.Fatal(err)
}
p, err := FromJson(rawData)
id, _ := v.CANId()
p, err := FromJson(id, rawData)
if err != nil {
t.Fatal(err)
}

View file

@ -1,58 +0,0 @@
package gotelem
import (
"encoding/json"
"fmt"
"os"
"time"
"github.com/kschamplin/gotelem/skylab"
)
// CanWriter
type CanWriter struct {
output *os.File
cd CANDumpEntry
jsonBuf []byte
}
// send writes the frame to the file.
func (cw *CanWriter) Send(f *Frame) (err error) {
cw.cd.Timestamp = float64(time.Now().Unix())
cw.cd.Id = uint64(f.Id)
cw.cd.Data, err = skylab.FromCanFrame(f.Id, f.Data)
if err != nil {
return
}
out, err := json.Marshal(cw.cd)
if err != nil {
return
}
fmt.Fprintln(cw.output, string(out))
return err
}
func (cw *CanWriter) Close() error {
return cw.output.Close()
}
func OpenCanWriter(name string) (*CanWriter, error) {
f, err := os.Create(name)
if err != nil {
return nil, err
}
cw := &CanWriter{
output: f,
}
return cw, nil
}
type CANDumpEntry struct {
Timestamp float64 `json:"ts"`
Id uint64 `json:"id"`
Data skylab.Packet `json:"data"`
}