diff --git a/cmd/fixer/fixer.go b/cmd/fixer/fixer.go deleted file mode 100644 index 6e3d6da..0000000 --- a/cmd/fixer/fixer.go +++ /dev/null @@ -1,206 +0,0 @@ -package main - -import ( - "encoding/json" - "errors" - "fmt" - "os" - "strings" - "syscall" - "time" - - "log/slog" - - "github.com/kschamplin/gotelem/skylab" - "github.com/urfave/cli/v2" -) - -// fixer resolves four major issues with CAN dumps: -// 1. ISO8601 timestamps -// 2. Unix seconds timestamps -// 3. Missing/broken timestamps (retime) -// 4. missing names - -func main() { - app := cli.NewApp() - app.Name = "fixer" - app.Usage = " fix skylabify outputs" - app.ArgsUsage = "" - app.Description = `fixer fixes four major issues with CAN dumps -1. ISO8601 timestamps --time iso8601 -2. Unix seconds timestamps --time seconds -3. Missing/broken timestamps --retime -4. missing names (enabled by default, --no-rename to skip) - ` - app.Flags = []cli.Flag{ - &cli.BoolFlag{ - Name: "retime", - Usage: "ignore timestamps and retime data based on 200ms heartbeat packet", - }, - &cli.StringFlag{ - Name: "format", - Usage: "Timestamp format. One of 'iso8601', 'seconds'. Ignored if using retime.", - }, - &cli.BoolFlag{ - Name: "no-rename", - Usage: "skip changing packet names", - }, - &cli.StringFlag{ - Name: "output", - Usage: "file to output to. defaults to stdout.", - }, - } - - app.Before = validateArgs - app.Action = run - app.HideHelp = true - app.Run(os.Args) -} - -var allowedFormats = []string{ - "iso8601", - "seconds", -} - -func checkFormat(input string) bool { - for _, allowed := range allowedFormats { - if input == allowed { - return true - } - } - return false -} - -func validateArgs(ctx *cli.Context) error { - if ctx.IsSet("format") { - format := ctx.String("format") - if !checkFormat(format) { - fmt.Printf("invalid format string, got %s, must be one of %s", format, strings.Join(allowedFormats, ", ")) - cli.ShowAppHelpAndExit(ctx, int(syscall.EINVAL)) - } - - } - if ctx.Args().Get(0) == "" { - fmt.Println("missing input file") - cli.ShowAppHelpAndExit(ctx, int(syscall.EINVAL)) - } - return nil -} - -func run(ctx *cli.Context) (err error) { - path := ctx.Args().Get(0) - - var istream *os.File - if path == "-" { - istream = os.Stdin - } else { - istream, err = os.Open(path) - if err != nil { - return - } - } - var ostream *os.File - oFilename := ctx.Args().Get(1) - if oFilename == "" { - ostream = os.Stdout - } else { - ostream, err = os.Create(oFilename) - if err != nil { - return - } - } - - iReader := json.NewDecoder(istream) - - shouldRetime := ctx.Bool("retime") - - // used for retime - increment by 200 ms every time we see a WsrStatusPacket - currentTick := time.Now().UnixMilli() - - for { - // read a line of json and fix names if we should. - - // FIXME: handle missing trailing newline. - // based on the fomat string, we should parse the data raw... - var res brokenCANMsg - err := iReader.Decode(&res) - - // float64 can have issues when represeneting - // unix milliseconds. the JSON decoder supports - // a "Number" struct that can be either. - iReader.UseNumber() - if err != nil { - return err - } - - var goodPkt skylab.RawJsonEvent - - goodPkt.Data = res.Data - goodPkt.Id = uint32(res.Id) - goodPkt.Name = res.Name - // wait to decode packet before rename. - - switch ts := res.Timestamp.(type) { - case json.Number: - // if it contains a decimal. - if strings.Contains(ts.String(), ".") { - // it's a float. - t, _ := ts.Float64() - t = t * 1000 - goodPkt.Timestamp = int64(t) - } else { - // it's an int. - t, _ := ts.Int64() - if ctx.String("format") == "seconds" { - t = t * 1000 - } - goodPkt.Timestamp = t - } - case string: - // parse as ISO8601 - // use unix millis - var t time.Time - err := t.UnmarshalText([]byte(ts)) - if err != nil { - panic(err) - } - goodPkt.Timestamp = t.UnixMilli() - - } - - if shouldRetime { - if goodPkt.Id == uint32(skylab.WsrStatusInformationId) { - // bump the clock 200ms - currentTick += 200 - } - goodPkt.Timestamp = currentTick - } - - // now, spit it out. - var bEv skylab.BusEvent - bEv.Timestamp = time.UnixMilli(goodPkt.Timestamp) - bEv.Id = goodPkt.Id - bEv.Data, err = skylab.FromJson(goodPkt.Id, goodPkt.Data) - var idErr *skylab.UnknownIdError - if errors.As(err, &idErr) { - // unknown id - slog.Info("unknown id", "err", err) - continue - } else if err != nil { - return err - } - - out, err := json.Marshal(&bEv) - if err != nil { - panic(err) - } - fmt.Fprintln(ostream, string(out)) - } -} - -type brokenCANMsg struct { - Timestamp any `json:"ts"` - Id int32 - Name string - Data json.RawMessage -}