gotelem/cmd/skylabify/skylabify.go

134 lines
2.8 KiB
Go
Raw Normal View History

2023-05-20 19:53:56 +00:00
package main
import (
"bufio"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strconv"
"strings"
"syscall"
2023-07-03 14:44:32 +00:00
"time"
2023-05-20 19:53:56 +00:00
"github.com/kschamplin/gotelem/skylab"
"github.com/urfave/cli/v2"
2023-06-28 01:39:57 +00:00
"golang.org/x/exp/slog"
2023-05-20 19:53:56 +00:00
)
// this command can be used to decode candump logs and dump json output.
func main() {
app := cli.NewApp()
app.Name = "skylabify"
app.Usage = "decode skylab packets"
app.ArgsUsage = "<input file>"
app.Commands = nil
app.Description = `skylabify can read in candump logs and output newline-delimited JSON.
It is designed to make reading candumps fast and easy.
skylabify can be combined with jq and candump to allow for advanced queries.
Examples:
skylabify candump.txt
candump -L can0 | skylabify -
skylabify previous_candump.txt | jq <some json query>
I highly suggest reading the manpages for candump and jq. The -L option is
required for piping candump into skylabify. Likewise, data should be stored with
-l.
`
app.Flags = []cli.Flag{
&cli.BoolFlag{
Name: "verbose",
Aliases: []string{"v"},
},
}
app.Action = run
app.HideHelp = true
if err := app.Run(os.Args); err != nil {
panic(err)
}
}
func run(ctx *cli.Context) (err error) {
path := ctx.Args().Get(0)
if path == "" {
fmt.Printf("missing input file\n")
cli.ShowAppHelpAndExit(ctx, int(syscall.EINVAL))
}
var istream *os.File
if path == "-" {
istream = os.Stdin
} else {
istream, err = os.Open(path)
if err != nil {
return
}
}
canDumpReader := bufio.NewReader(istream)
for {
// dumpline looks like this:
// (1684538768.521889) can0 200#8D643546
dumpLine, err := canDumpReader.ReadString('\n')
if err != nil {
if errors.Is(err, io.EOF) {
return nil
}
return err
}
// remove trailing newline
dumpLine = strings.TrimSpace(dumpLine)
segments := strings.Split(dumpLine, " ")
2023-05-29 00:39:03 +00:00
var cd skylab.BusEvent
2023-07-03 18:51:15 +00:00
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.
2023-05-20 19:53:56 +00:00
// this is for the latter part, we need to split id/data
hexes := strings.Split(segments[2], "#")
// get the id
2023-06-30 04:59:16 +00:00
id, err := strconv.ParseUint(hexes[0], 16, 64)
2023-05-20 19:53:56 +00:00
if err != nil {
return err
}
2023-06-30 04:59:16 +00:00
cd.Id = uint32(id)
2023-05-20 19:53:56 +00:00
// 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)
2023-06-28 01:39:57 +00:00
var idErr *skylab.UnknownIdError
if errors.As(err, &idErr) {
// unknown id
slog.Info("unknown id", "err", err)
2023-07-01 03:08:06 +00:00
continue
2023-06-28 01:39:57 +00:00
} else if err != nil {
2023-05-20 19:53:56 +00:00
return err
}
2023-05-22 13:50:07 +00:00
// format and print out the JSON.
2023-07-01 03:08:06 +00:00
out, _ := json.Marshal(&cd)
2023-05-22 13:50:07 +00:00
fmt.Println(string(out))
2023-05-20 19:53:56 +00:00
}
}