major wins
This commit is contained in:
parent
eefc511f4f
commit
47a818a451
|
@ -7,14 +7,17 @@ import (
|
|||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var subCmds = []*cli.Command{
|
||||
serveCmd,
|
||||
xbeeCmd,
|
||||
}
|
||||
|
||||
|
||||
func Execute() {
|
||||
app := &cli.App{
|
||||
Name: "gotelem",
|
||||
Usage: "see everything",
|
||||
Commands: []*cli.Command{
|
||||
serveCmd,
|
||||
xbeeCmd,
|
||||
},
|
||||
Commands: subCmds,
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
|
|
|
@ -15,34 +15,45 @@ import (
|
|||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
var serveCmd = &cli.Command{
|
||||
Name: "serve",
|
||||
Aliases: []string{"server", "s"},
|
||||
Usage: "Start a telemetry server",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{Name: "test", Usage: "use vcan0 test"},
|
||||
var serveFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "device",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "The XBee to connect to",
|
||||
EnvVars: []string{"XBEE_DEVICE"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "can",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "CAN device string",
|
||||
EnvVars: []string{"CAN_DEVICE"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "logfile",
|
||||
Aliases: []string{"l"},
|
||||
Value: "log.txt",
|
||||
Usage: "file to store log to",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var serveCmd = &cli.Command{
|
||||
Name: "serve",
|
||||
Aliases: []string{"server", "s"},
|
||||
Usage: "Start a telemetry server",
|
||||
Flags: serveFlags,
|
||||
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) (err error)
|
||||
|
||||
type service interface {
|
||||
fmt.Stringer
|
||||
Start(cCtx *cli.Context, broker *gotelem.Broker) (err error)
|
||||
Status()
|
||||
}
|
||||
|
||||
// this variable stores all the hanlders. It has some basic ones, but also
|
||||
// can be extended on certain platforms (see cli/socketcan.go)
|
||||
// or if certain features are present (see sqlite.go)
|
||||
var serveThings = []testThing{}
|
||||
|
||||
func serve(cCtx *cli.Context) error {
|
||||
// TODO: output both to stderr and a file.
|
||||
logger := slog.New(slog.NewTextHandler(os.Stderr))
|
||||
|
@ -55,7 +66,6 @@ func serve(cCtx *cli.Context) error {
|
|||
// can logger.
|
||||
go CanDump(broker, logger.WithGroup("candump"), done)
|
||||
|
||||
|
||||
if cCtx.String("device") != "" {
|
||||
logger.Info("using xbee device")
|
||||
transport, err := xbee.ParseDeviceString(cCtx.String("device"))
|
||||
|
@ -93,10 +103,25 @@ func serve(cCtx *cli.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func tcpSvc(ctx *cli.Context, broker *gotelem.Broker) error {
|
||||
// TODO: extract port/ip from cli context.
|
||||
ln, err := net.Listen("tcp", ":8082")
|
||||
if err != nil {
|
||||
fmt.Printf("Error listening: %v\n", err)
|
||||
}
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
fmt.Printf("error accepting: %v\n", err)
|
||||
}
|
||||
go handleCon(conn, broker, slog.Default().WithGroup("tcp"), ctx.Done())
|
||||
}
|
||||
}
|
||||
|
||||
func handleCon(conn net.Conn, broker *gotelem.Broker, l *slog.Logger, done <-chan struct{}) {
|
||||
// reader := msgp.NewReader(conn)
|
||||
|
||||
|
||||
subname := fmt.Sprint("tcp", conn.RemoteAddr().String())
|
||||
|
||||
l.Info("started handling", "name", subname)
|
||||
|
@ -124,10 +149,8 @@ func handleCon(conn net.Conn, broker *gotelem.Broker, l *slog.Logger, done <-cha
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// this spins up a new can socket on vcan0 and broadcasts a packet every second. for testing.
|
||||
func vcanTest(devname string) {
|
||||
sock, err := socketcan.NewCanSocket(devname)
|
||||
|
@ -152,7 +175,6 @@ func vcanTest(devname string) {
|
|||
func canHandler(broker *gotelem.Broker, l *slog.Logger, done <-chan struct{}, devname string) {
|
||||
rxCh := broker.Subscribe("socketcan")
|
||||
sock, err := socketcan.NewCanSocket(devname)
|
||||
|
||||
if err != nil {
|
||||
l.Error("error opening socket", "err", err)
|
||||
return
|
||||
|
@ -208,13 +230,11 @@ func CanDump(broker *gotelem.Broker, l *slog.Logger, done <-chan struct{}) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func XBeeSend(broker *gotelem.Broker, l *slog.Logger, done <-chan struct{}, trspt *xbee.Transport) {
|
||||
rxCh := broker.Subscribe("xbee")
|
||||
l.Info("starting xbee send routine")
|
||||
|
||||
xb, err := xbee.NewSession(trspt, l.With("device", trspt.Type()))
|
||||
|
||||
if err != nil {
|
||||
l.Error("failed to start xbee session", "err", err)
|
||||
return
|
||||
|
@ -242,6 +262,4 @@ func XBeeSend(broker *gotelem.Broker, l *slog.Logger, done <-chan struct{}, trsp
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
96
cmd/gotelem/cli/socketcan.go
Normal file
96
cmd/gotelem/cli/socketcan.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
//go:build linux
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/kschamplin/gotelem"
|
||||
"github.com/kschamplin/gotelem/socketcan"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// this file adds socketCAN commands and functionality when building on linux.
|
||||
// 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"},
|
||||
DefaultText: "vcan0",
|
||||
}
|
||||
|
||||
// this function sets up the `serve` flags and services that use socketCAN
|
||||
func init() {
|
||||
serveFlags = append(serveFlags, &cli.BoolFlag{Name: "test", Usage: "use vcan0 test"})
|
||||
serveFlags = append(serveFlags, canDevFlag)
|
||||
// add services for server
|
||||
|
||||
serveThings = append(serveThings, socketCANService)
|
||||
|
||||
// add can subcommand/actions
|
||||
// TODO: make socketcan utility commands.
|
||||
}
|
||||
|
||||
|
||||
// FIXME: add logging back in since it's missing rn
|
||||
|
||||
func socketCANService(cCtx *cli.Context, broker *gotelem.Broker) (err error) {
|
||||
rxCh := broker.Subscribe("socketCAN")
|
||||
sock, err := socketcan.NewCanSocket(cCtx.String("can"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rxCan := make(chan gotelem.Frame)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
pkt, _ := sock.Recv()
|
||||
rxCan <- *pkt
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case msg := <-rxCh:
|
||||
sock.Send(&msg)
|
||||
case msg := <-rxCan:
|
||||
broker.Publish("socketCAN", msg)
|
||||
case <-cCtx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
var socketCANCmd = &cli.Command{
|
||||
Name: "can",
|
||||
Usage: "SocketCAN utilities",
|
||||
Description: `
|
||||
Various helper utilties for CAN bus on sockets.
|
||||
|
||||
`,
|
||||
Flags: []cli.Flag{
|
||||
canDevFlag,
|
||||
},
|
||||
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "dump",
|
||||
Usage: "dump CAN packets to stdout",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "decode",
|
||||
Usage: "unpack known CAN packets to JSON using skylab files",
|
||||
Value: false,
|
||||
},
|
||||
},
|
||||
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -152,6 +152,12 @@ func (d *DataField) MakeMarshal(offset int) string {
|
|||
|
||||
} 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"
|
||||
|
@ -169,9 +175,15 @@ func (d *DataField) MakeUnmarshal(offset int) string {
|
|||
|
||||
return fmt.Sprintf("p.%s = float32FromBytes(b[%d:], false)", fieldName, offset)
|
||||
|
||||
} 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 read it.
|
||||
// FIXME: support big endian
|
||||
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("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")
|
||||
|
@ -323,17 +335,22 @@ func main() {
|
|||
|
||||
fmt.Printf("running %s on %s\n", os.Args[0], os.Getenv("GOFILE"))
|
||||
|
||||
fmt.Printf("skylab packet definition path is %s\n", os.Args[1])
|
||||
basePath, err := filepath.Abs(os.Args[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("skylab packet definition path is %s\n", basePath)
|
||||
|
||||
fGlob := filepath.Join(os.Args[1], "*.y?ml")
|
||||
fGlob := filepath.Join(basePath, "*.y?ml")
|
||||
files, err := filepath.Glob(fGlob)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("found %d files\n", len(files))
|
||||
for _, f := range files {
|
||||
fd, err := os.Open(f)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to open file %s:%v\n", f, err)
|
||||
fmt.Printf("failed to open file %s:%v\n", filepath.Base(f), err)
|
||||
}
|
||||
dec := yaml.NewDecoder(fd)
|
||||
newFile := &SkylabFile{}
|
||||
|
@ -341,7 +358,7 @@ func main() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("adding %d packets and %d boards\n", len(newFile.Packets), len(newFile.Boards))
|
||||
fmt.Printf("%s: adding %d packets and %d boards\n", filepath.Base(f), len(newFile.Packets), len(newFile.Boards))
|
||||
v.Packets = append(v.Packets, newFile.Packets...)
|
||||
v.Boards = append(v.Boards, newFile.Boards...)
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ func float32FromBytes(b []byte, bigEndian bool) (f float32) {
|
|||
type Packet interface {
|
||||
MarshalPacket() ([]byte, error)
|
||||
UnmarshalPacket(p []byte) error
|
||||
Id() (uint32, error)
|
||||
CANId() (uint32, error)
|
||||
Size() uint
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ type Unmarshaler interface {
|
|||
|
||||
// Ider is a packet that can get its ID, based on the index of the packet, if any.
|
||||
type Ider interface {
|
||||
Id() (uint32, error)
|
||||
CANId() (uint32, error)
|
||||
}
|
||||
|
||||
// Sizer allows for fast allocation.
|
||||
|
@ -63,7 +63,7 @@ type Sizer interface {
|
|||
// CanSend takes a packet and makes CAN framing data.
|
||||
func CanSend(p Packet) (id uint32, data []byte, err error) {
|
||||
|
||||
id, err = p.Id()
|
||||
id, err = p.CANId()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ func ToJson(p Packet) (*JSONPacket, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
id, err := p.Id()
|
||||
id, err := p.CANId()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -34,7 +34,9 @@ func (p *{{$bfname}}) Unmarshal(b byte) {
|
|||
// {{$structName}} is {{.Description}}
|
||||
type {{$structName}} struct {
|
||||
{{- range .Data}}
|
||||
{{- if .Units -}} // {{.Conversion}} {{.Units}} {{- end }}
|
||||
{{- if .Units }}
|
||||
// {{.Conversion}} {{.Units}}
|
||||
{{- end }}
|
||||
{{ .ToStructMember $structName }} `json:"{{.Name}}"`
|
||||
{{- end}}
|
||||
{{- if .Repeat }}
|
||||
|
@ -43,7 +45,7 @@ type {{$structName}} struct {
|
|||
{{- end }}
|
||||
}
|
||||
|
||||
func (p *{{$structName}}) Id() (uint32, error) {
|
||||
func (p *{{$structName}}) CANId() (uint32, error) {
|
||||
{{- if .Repeat }}
|
||||
if p.Idx >= {{.Repeat}} {
|
||||
return 0, errors.New("invalid packet index")
|
||||
|
|
Loading…
Reference in a new issue