cli work
This commit is contained in:
parent
86c439438d
commit
a9a11c5456
43
cmd/gotelem/cli/client.go
Normal file
43
cmd/gotelem/cli/client.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/kschamplin/gotelem"
|
||||||
|
"github.com/kschamplin/gotelem/mprpc"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
subCmds = append(subCmds, clientCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var clientCmd = &cli.Command{
|
||||||
|
Name: "client",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "interact with a gotelem server",
|
||||||
|
ArgsUsage: "[server url]",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "gui",
|
||||||
|
Aliases: []string{"g"},
|
||||||
|
Usage: "start a local TUI",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Description: `
|
||||||
|
Connects to a gotelem server or relay. Can be used to
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
var initialRPCHandlers = map[string]mprpc.ServiceFunc{
|
||||||
|
"can": mprpc.MakeService(CANFrameHandler),
|
||||||
|
}
|
|
@ -5,11 +5,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kschamplin/gotelem"
|
"github.com/kschamplin/gotelem"
|
||||||
"github.com/kschamplin/gotelem/socketcan"
|
|
||||||
"github.com/kschamplin/gotelem/xbee"
|
"github.com/kschamplin/gotelem/xbee"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"golang.org/x/exp/slog"
|
"golang.org/x/exp/slog"
|
||||||
|
@ -19,7 +17,7 @@ var serveFlags = []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "device",
|
Name: "device",
|
||||||
Aliases: []string{"d"},
|
Aliases: []string{"d"},
|
||||||
Usage: "The XBee to connect to",
|
Usage: "The XBee to connect to. Leave blank to not use XBee",
|
||||||
EnvVars: []string{"XBEE_DEVICE"},
|
EnvVars: []string{"XBEE_DEVICE"},
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
|
@ -38,10 +36,11 @@ var serveCmd = &cli.Command{
|
||||||
Action: serve,
|
Action: serve,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// FIXME: naming
|
// FIXME: naming
|
||||||
// this is a server handler for i.e tcp socket, http server, socketCAN, xbee,
|
// this is a server handler for i.e tcp socket, http server, socketCAN, xbee,
|
||||||
// etc. we can register them in init() functions.
|
// etc. we can register them in init() functions.
|
||||||
type testThing func(cCtx *cli.Context, broker *gotelem.Broker) (err error)
|
type testThing func(cCtx *cli.Context, broker *gotelem.Broker, logger *slog.Logger) (err error)
|
||||||
|
|
||||||
type service interface {
|
type service interface {
|
||||||
fmt.Stringer
|
fmt.Stringer
|
||||||
|
@ -51,9 +50,16 @@ type service interface {
|
||||||
|
|
||||||
// this variable stores all the hanlders. It has some basic ones, but also
|
// this variable stores all the hanlders. It has some basic ones, but also
|
||||||
// can be extended on certain platforms (see cli/socketcan.go)
|
// can be extended on certain platforms (see cli/socketcan.go)
|
||||||
// or if certain features are present (see sqlite.go)
|
// or if certain features are present (see cli/sqlite.go)
|
||||||
var serveThings = []service{
|
var serveThings = []service{
|
||||||
&XBeeService{},
|
&XBeeService{},
|
||||||
|
&CanLoggerService{},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func deriveLogger (oldLogger *slog.Logger, svc service) (newLogger *slog.Logger) {
|
||||||
|
newLogger = oldLogger.With("svc", svc.String())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func serve(cCtx *cli.Context) error {
|
func serve(cCtx *cli.Context) error {
|
||||||
|
@ -65,20 +71,21 @@ func serve(cCtx *cli.Context) error {
|
||||||
|
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
// start the can listener
|
// start the can listener
|
||||||
// can logger.
|
|
||||||
go CanDump(broker, logger.WithGroup("candump"), done)
|
|
||||||
|
|
||||||
if cCtx.String("can") != "" {
|
|
||||||
logger.Info("using can device")
|
|
||||||
go canHandler(broker, logger.With("device", cCtx.String("can")), done, cCtx.String("can"))
|
|
||||||
|
|
||||||
if strings.HasPrefix(cCtx.String("can"), "v") {
|
|
||||||
go vcanTest(cCtx.String("can"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
go broker.Start()
|
go broker.Start()
|
||||||
|
|
||||||
|
|
||||||
|
for _, svc := range serveThings {
|
||||||
|
svcLogger := deriveLogger(logger, svc)
|
||||||
|
logger.Info("starting service", "svc", svc.String())
|
||||||
|
go func(mySvc service) {
|
||||||
|
err := mySvc.Start(cCtx, broker, svcLogger)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("service stopped!", "err", err, "svc", mySvc.String())
|
||||||
|
}
|
||||||
|
}(svc)
|
||||||
|
}
|
||||||
|
|
||||||
// tcp listener server.
|
// tcp listener server.
|
||||||
ln, err := net.Listen("tcp", ":8082")
|
ln, err := net.Listen("tcp", ":8082")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -95,18 +102,24 @@ func serve(cCtx *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func tcpSvc(ctx *cli.Context, broker *gotelem.Broker) error {
|
|
||||||
|
type rpcService struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcpSvc(ctx *cli.Context, broker *gotelem.Broker, logger *slog.Logger) error {
|
||||||
// TODO: extract port/ip from cli context.
|
// TODO: extract port/ip from cli context.
|
||||||
ln, err := net.Listen("tcp", ":8082")
|
ln, err := net.Listen("tcp", ":8082")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error listening: %v\n", err)
|
fmt.Printf("Error listening: %v\n", err)
|
||||||
|
logger.Warn("error listening", "err", err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
conn, err := ln.Accept()
|
conn, err := ln.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("error accepting: %v\n", err)
|
logger.Warn("error accepting connection", "err", err)
|
||||||
}
|
}
|
||||||
go handleCon(conn, broker, slog.Default().WithGroup("tcp"), ctx.Done())
|
go handleCon(conn, broker, logger.With("addr", conn.RemoteAddr()), ctx.Done())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,70 +156,31 @@ 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.
|
// 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)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("error opening socket", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
testFrame := &gotelem.Frame{
|
|
||||||
Id: 0x234,
|
|
||||||
Kind: gotelem.CanSFFFrame,
|
|
||||||
Data: []byte{0, 1, 2, 3, 4, 5, 6, 7},
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
|
|
||||||
slog.Info("sending test packet")
|
type CanLoggerService struct {
|
||||||
sock.Send(testFrame)
|
cw gotelem.CanWriter
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// connects the broker to a socket can
|
func (c *CanLoggerService) String() string {
|
||||||
func canHandler(broker *gotelem.Broker, l *slog.Logger, done <-chan struct{}, devname string) {
|
return "CanLoggerService"
|
||||||
rxCh := broker.Subscribe("socketcan")
|
|
||||||
sock, err := socketcan.NewCanSocket(devname)
|
|
||||||
if err != nil {
|
|
||||||
l.Error("error opening socket", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// start a simple dispatcher that just relays can frames.
|
|
||||||
rxCan := make(chan gotelem.Frame)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
pkt, err := sock.Recv()
|
|
||||||
if err != nil {
|
|
||||||
l.Warn("error reading SocketCAN", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rxCan <- *pkt
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case msg := <-rxCh:
|
|
||||||
l.Info("Sending a CAN bus message", "id", msg.Id, "data", msg.Data)
|
|
||||||
sock.Send(&msg)
|
|
||||||
case msg := <-rxCan:
|
|
||||||
l.Info("Got a CAN bus message", "id", msg.Id, "data", msg.Data)
|
|
||||||
broker.Publish("socketcan", msg)
|
|
||||||
case <-done:
|
|
||||||
sock.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CanDump(broker *gotelem.Broker, l *slog.Logger, done <-chan struct{}) {
|
func (c *CanLoggerService) Status() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (c *CanLoggerService) Start(cCtx *cli.Context, broker *gotelem.Broker, l *slog.Logger) (err error) {
|
||||||
rxCh := broker.Subscribe("candump")
|
rxCh := broker.Subscribe("candump")
|
||||||
t := time.Now()
|
t := time.Now()
|
||||||
fname := fmt.Sprintf("candump_%d-%02d-%02dT%02d.%02d.%02d.txt",
|
fname := fmt.Sprintf("candump_%d-%02d-%02dT%02d.%02d.%02d.txt",
|
||||||
t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
|
t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
|
||||||
|
|
||||||
|
l.Info("logging to file", "filename", fname)
|
||||||
|
|
||||||
cw, err := gotelem.OpenCanWriter(fname)
|
cw, err := gotelem.OpenCanWriter(fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("error opening file", "err", err)
|
l.Error("error opening file", "filename", fname, "err", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -214,7 +188,7 @@ func CanDump(broker *gotelem.Broker, l *slog.Logger, done <-chan struct{}) {
|
||||||
case msg := <-rxCh:
|
case msg := <-rxCh:
|
||||||
|
|
||||||
cw.Send(&msg)
|
cw.Send(&msg)
|
||||||
case <-done:
|
case <-cCtx.Done():
|
||||||
cw.Close()
|
cw.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -232,9 +206,7 @@ func (x *XBeeService) Status() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (x *XBeeService) Start(cCtx *cli.Context,
|
func (x *XBeeService) Start(cCtx *cli.Context, broker *gotelem.Broker, logger *slog.Logger) (err error) {
|
||||||
broker *gotelem.Broker, logger *slog.Logger,
|
|
||||||
) (err error) {
|
|
||||||
if cCtx.String("xbee") == "" {
|
if cCtx.String("xbee") == "" {
|
||||||
logger.Info("not using xbee")
|
logger.Info("not using xbee")
|
||||||
return
|
return
|
||||||
|
@ -260,7 +232,6 @@ func (x *XBeeService) Start(cCtx *cli.Context,
|
||||||
x.session.Close()
|
x.session.Close()
|
||||||
return
|
return
|
||||||
case msg := <-rxCh:
|
case msg := <-rxCh:
|
||||||
// TODO: take can message and send it over CAN.
|
|
||||||
logger.Info("got msg", "msg", msg)
|
logger.Info("got msg", "msg", msg)
|
||||||
buf := make([]byte, 0)
|
buf := make([]byte, 0)
|
||||||
|
|
||||||
|
@ -275,6 +246,5 @@ func (x *XBeeService) Start(cCtx *cli.Context,
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kschamplin/gotelem"
|
"github.com/kschamplin/gotelem"
|
||||||
"github.com/kschamplin/gotelem/socketcan"
|
"github.com/kschamplin/gotelem/socketcan"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
@ -12,12 +15,11 @@ import (
|
||||||
// this file adds socketCAN commands and functionality when building on linux.
|
// 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.
|
// It is an example of the modular architecture of the command line and server stack.
|
||||||
|
|
||||||
|
|
||||||
var canDevFlag = &cli.StringFlag{
|
var canDevFlag = &cli.StringFlag{
|
||||||
Name: "can",
|
Name: "can",
|
||||||
Aliases: []string{"c"},
|
Aliases: []string{"c"},
|
||||||
Usage: "CAN device string",
|
Usage: "CAN device string",
|
||||||
EnvVars: []string{"CAN_DEVICE"},
|
EnvVars: []string{"CAN_DEVICE"},
|
||||||
DefaultText: "vcan0",
|
DefaultText: "vcan0",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,11 +36,10 @@ func init() {
|
||||||
subCmds = append(subCmds, socketCANCmd)
|
subCmds = append(subCmds, socketCANCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// FIXME: add logging back in since it's missing rn
|
// FIXME: add logging back in since it's missing rn
|
||||||
|
|
||||||
|
|
||||||
type socketCANService struct {
|
type socketCANService struct {
|
||||||
|
sock socketcan.CanSocket
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *socketCANService) Status() {
|
func (s *socketCANService) Status() {
|
||||||
|
@ -50,9 +51,16 @@ func (s *socketCANService) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *socketCANService) Start(cCtx *cli.Context, broker *gotelem.Broker, logger *slog.Logger) (err error) {
|
func (s *socketCANService) Start(cCtx *cli.Context, broker *gotelem.Broker, logger *slog.Logger) (err error) {
|
||||||
|
// vcan0 demo
|
||||||
|
|
||||||
|
if strings.HasPrefix(cCtx.String("can"), "v") {
|
||||||
|
go vcanTest(cCtx.String("can"))
|
||||||
|
}
|
||||||
|
|
||||||
rxCh := broker.Subscribe("socketCAN")
|
rxCh := broker.Subscribe("socketCAN")
|
||||||
sock, err := socketcan.NewCanSocket(cCtx.String("can"))
|
sock, err := socketcan.NewCanSocket(cCtx.String("can"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logger.Error("error opening socket", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +74,6 @@ func (s *socketCANService) Start(cCtx *cli.Context, broker *gotelem.Broker, logg
|
||||||
}
|
}
|
||||||
rxCan <- *pkt
|
rxCan <- *pkt
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -79,12 +86,10 @@ func (s *socketCANService) Start(cCtx *cli.Context, broker *gotelem.Broker, logg
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var socketCANCmd = &cli.Command{
|
var socketCANCmd = &cli.Command{
|
||||||
Name: "can",
|
Name: "can",
|
||||||
Usage: "SocketCAN utilities",
|
Usage: "SocketCAN utilities",
|
||||||
Description: `
|
Description: `
|
||||||
Various helper utilties for CAN bus on sockets.
|
Various helper utilties for CAN bus on sockets.
|
||||||
|
@ -95,18 +100,34 @@ Various helper utilties for CAN bus on sockets.
|
||||||
|
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
{
|
{
|
||||||
Name: "dump",
|
Name: "dump",
|
||||||
Usage: "dump CAN packets to stdout",
|
Usage: "dump CAN packets to stdout",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "decode",
|
Name: "decode",
|
||||||
Usage: "unpack known CAN packets to JSON using skylab files",
|
Usage: "unpack known CAN packets to JSON using skylab files",
|
||||||
Value: false,
|
Value: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vcanTest(devname string) {
|
||||||
|
sock, err := socketcan.NewCanSocket(devname)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("error opening socket", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
testFrame := &gotelem.Frame{
|
||||||
|
Id: 0x234,
|
||||||
|
Kind: gotelem.CanSFFFrame,
|
||||||
|
Data: []byte{0, 1, 2, 3, 4, 5, 6, 7},
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
|
||||||
|
slog.Info("sending test packet")
|
||||||
|
sock.Send(testFrame)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue