migrate candump log parser to use regex instead
Some checks failed
Go / build (push) Failing after 1m10s
Some checks failed
Go / build (push) Failing after 1m10s
also add more test cases for error logic.
This commit is contained in:
parent
481cac76c6
commit
c95593bb86
|
@ -30,57 +30,66 @@ func (e *FormatError) Unwrap() error {
|
||||||
return e.err
|
return e.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewFormatError constructs a new format error.
|
||||||
func NewFormatError(msg string, err error) error {
|
func NewFormatError(msg string, err error) error {
|
||||||
return &FormatError{msg: msg, err: err}
|
return &FormatError{msg: msg, err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// type LineParserFunc is a function that takes a string
|
// type LineParserFunc is a function that takes a string
|
||||||
// and returns a can frame. This is useful for common
|
// and returns a can frame. This is useful for common
|
||||||
// can dump formats.
|
// can dump formats.
|
||||||
type LineParserFunc func(string) (can.Frame, time.Time, error)
|
type LineParserFunc func(string) (can.Frame, time.Time, error)
|
||||||
|
|
||||||
var candumpRegex = regexp.MustCompile(`^\((\d+)\.(\d{6}) \w+ (\w+)#(\w+)$`)
|
var candumpRegex = regexp.MustCompile(`^\((\d+)\.(\d{6})\) \w+ (\w+)#(\w+)$`)
|
||||||
|
|
||||||
func parseCanDumpLine(dumpLine string) (frame can.Frame, ts time.Time, err error) {
|
func parseCanDumpLine(dumpLine string) (frame can.Frame, ts time.Time, err error) {
|
||||||
frame = can.Frame{}
|
frame = can.Frame{}
|
||||||
ts = time.Unix(0,0)
|
ts = time.Unix(0,0)
|
||||||
// dumpline looks like this:
|
// dumpline looks like this:
|
||||||
// (1684538768.521889) can0 200#8D643546
|
// (1684538768.521889) can0 200#8D643546
|
||||||
// remove trailing newline
|
// remove trailing newline/whitespaces
|
||||||
dumpLine = strings.TrimSpace(dumpLine)
|
dumpLine = strings.TrimSpace(dumpLine)
|
||||||
segments := strings.Split(dumpLine, " ")
|
m := candumpRegex.FindStringSubmatch(dumpLine)
|
||||||
if len(segments) != 3 {
|
if m == nil || len(m) != 5 {
|
||||||
err = NewFormatError("failed to split line", err)
|
err = NewFormatError("no regex match", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var unixSeconds, unixMicros int64
|
var unixSeconds, unixMicros int64
|
||||||
fmt.Sscanf(segments[0], "(%d.%d)", &unixSeconds, &unixMicros)
|
|
||||||
|
|
||||||
// now we extract the remaining data:
|
unixSeconds, err = strconv.ParseInt(m[1], 10, 0)
|
||||||
hexes := strings.Split(segments[2], "#") // first portion is id, second is data
|
if err != nil {
|
||||||
|
err = NewFormatError("failed to parse unix seconds", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
unixMicros, err = strconv.ParseInt(m[2], 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
err = NewFormatError("failed to parse unix micros", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
id, err := strconv.ParseUint(hexes[0], 16, 64)
|
id, err := strconv.ParseUint(m[3], 16, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = NewFormatError("failed to parse id", err)
|
err = NewFormatError("failed to parse id", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (len(hexes[1]) % 2) != 0 {
|
if (len(m[4]) % 2) != 0 {
|
||||||
err = NewFormatError("odd number of hex characters", nil)
|
err = NewFormatError("odd number of hex characters", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rawData, err := hex.DecodeString(hexes[1])
|
rawData, err := hex.DecodeString(m[4])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = NewFormatError("failed to decode hex data", err)
|
err = NewFormatError("failed to decode hex data", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: add extended id support, need an example log and a test.
|
||||||
frame.Id = can.CanID{Id: uint32(id), Extended: false}
|
frame.Id = can.CanID{Id: uint32(id), Extended: false}
|
||||||
frame.Data = rawData
|
frame.Data = rawData
|
||||||
frame.Kind = can.CanDataFrame
|
frame.Kind = can.CanDataFrame
|
||||||
|
|
||||||
ts = time.Unix(unixSeconds, unixMicros)
|
ts = time.Unix(unixSeconds, unixMicros * int64(time.Microsecond))
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -156,10 +165,11 @@ func parseTelemLogLine(line string) (frame can.Frame, ts time.Time, err error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is how we adapt a can frame source into one that produces
|
// BusParserFunc is a function that takes a string and returns a busevent.
|
||||||
// skylab busevents
|
|
||||||
type BusParserFunc func(string) (skylab.BusEvent, error)
|
type BusParserFunc func(string) (skylab.BusEvent, error)
|
||||||
|
|
||||||
|
// parserBusEventMapper takes a line parser (that returns a can frame)
|
||||||
|
// and makes it return a busEvent instead.
|
||||||
func parserBusEventMapper(f LineParserFunc) BusParserFunc {
|
func parserBusEventMapper(f LineParserFunc) BusParserFunc {
|
||||||
return func(s string) (skylab.BusEvent, error) {
|
return func(s string) (skylab.BusEvent, error) {
|
||||||
var b = skylab.BusEvent{}
|
var b = skylab.BusEvent{}
|
||||||
|
|
|
@ -19,13 +19,6 @@ func Test_parseCanDumpLine(t *testing.T) {
|
||||||
wantTs time.Time
|
wantTs time.Time
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
name: "test garbage",
|
|
||||||
args: args{dumpLine: "hosireoie"},
|
|
||||||
wantFrame: can.Frame{},
|
|
||||||
wantTs: time.Unix(0,0),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "test normal data",
|
name: "test normal data",
|
||||||
args: args{dumpLine: "(1684538768.521889) can0 200#8D643546"},
|
args: args{dumpLine: "(1684538768.521889) can0 200#8D643546"},
|
||||||
|
@ -34,40 +27,16 @@ func Test_parseCanDumpLine(t *testing.T) {
|
||||||
Data: []byte{0x8d, 0x64, 0x35, 0x46},
|
Data: []byte{0x8d, 0x64, 0x35, 0x46},
|
||||||
Kind: can.CanDataFrame,
|
Kind: can.CanDataFrame,
|
||||||
},
|
},
|
||||||
wantTs: time.Unix(1684538768, 521889),
|
wantTs: time.Unix(1684538768, 521889 * int64(time.Microsecond)),
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "bad data length",
|
|
||||||
// odd number of hex data nibbles
|
|
||||||
args: args{dumpLine: "(1684538768.521889) can0 200#8D64354"},
|
|
||||||
wantFrame: can.Frame{},
|
|
||||||
wantTs: time.Unix(0,0),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid hex",
|
|
||||||
// J is not valid hex.
|
|
||||||
args: args{dumpLine: "(1684538768.521889) can0 200#8D64354J"},
|
|
||||||
wantFrame: can.Frame{},
|
|
||||||
wantTs: time.Unix(0,0),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "bad time",
|
|
||||||
// we destroy the time structure.
|
|
||||||
args: args{dumpLine: "(badtime.521889) can0 200#8D643546"},
|
|
||||||
wantFrame: can.Frame{},
|
|
||||||
wantTs: time.Unix(0,0),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
// TODO: add extended id test case
|
// TODO: add extended id test case
|
||||||
|
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
gotFrame, gotTs, err := parseCanDumpLine(tt.args.dumpLine)
|
gotFrame, gotTs, err := parseCanDumpLine(tt.args.dumpLine)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err == nil) == tt.wantErr {
|
||||||
t.Errorf("parseCanDumpLine() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("parseCanDumpLine() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -80,3 +49,47 @@ func Test_parseCanDumpLine(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_parseCanDumpLine_errors(t *testing.T) {
|
||||||
|
// this test tries a bunch of failure cases to ensure that they are caught and not panicking.
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "garbage input",
|
||||||
|
input: "hoiseorhijkl",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad data length",
|
||||||
|
// odd number of hex data nibbles
|
||||||
|
input: "(1684538768.521889) can0 200#8D64354",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid hex",
|
||||||
|
// J is not valid hex.
|
||||||
|
input: "(1684538768.521889) can0 200#8D64354J",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad time",
|
||||||
|
// we destroy the time structure.
|
||||||
|
input: "(badtime.521889) can0 200#8D643546",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "utf8 corruption",
|
||||||
|
// we attempt to mess up the data with broken utf8
|
||||||
|
input: "(1684538768.521889) can0 200#8D6\xed\xa0\x8043546",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
f, ts, err := parseCanDumpLine(tt.input)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("parseCanDumpLine() expected error but instead got f = %v, ts = %v", f, ts)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue