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
|
||||
}
|
||||
|
||||
// NewFormatError constructs a new format error.
|
||||
func NewFormatError(msg string, err error) error {
|
||||
return &FormatError{msg: msg, err: err}
|
||||
}
|
||||
|
||||
|
||||
// type LineParserFunc is a function that takes a string
|
||||
// and returns a can frame. This is useful for common
|
||||
// can dump formats.
|
||||
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) {
|
||||
frame = can.Frame{}
|
||||
ts = time.Unix(0,0)
|
||||
// dumpline looks like this:
|
||||
// (1684538768.521889) can0 200#8D643546
|
||||
// remove trailing newline
|
||||
// remove trailing newline/whitespaces
|
||||
dumpLine = strings.TrimSpace(dumpLine)
|
||||
segments := strings.Split(dumpLine, " ")
|
||||
if len(segments) != 3 {
|
||||
err = NewFormatError("failed to split line", err)
|
||||
m := candumpRegex.FindStringSubmatch(dumpLine)
|
||||
if m == nil || len(m) != 5 {
|
||||
err = NewFormatError("no regex match", nil)
|
||||
return
|
||||
}
|
||||
|
||||
var unixSeconds, unixMicros int64
|
||||
fmt.Sscanf(segments[0], "(%d.%d)", &unixSeconds, &unixMicros)
|
||||
|
||||
// now we extract the remaining data:
|
||||
hexes := strings.Split(segments[2], "#") // first portion is id, second is data
|
||||
unixSeconds, err = strconv.ParseInt(m[1], 10, 0)
|
||||
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 {
|
||||
err = NewFormatError("failed to parse id", err)
|
||||
return
|
||||
}
|
||||
if (len(hexes[1]) % 2) != 0 {
|
||||
if (len(m[4]) % 2) != 0 {
|
||||
err = NewFormatError("odd number of hex characters", nil)
|
||||
return
|
||||
}
|
||||
rawData, err := hex.DecodeString(hexes[1])
|
||||
rawData, err := hex.DecodeString(m[4])
|
||||
if err != nil {
|
||||
err = NewFormatError("failed to decode hex data", err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: add extended id support, need an example log and a test.
|
||||
frame.Id = can.CanID{Id: uint32(id), Extended: false}
|
||||
frame.Data = rawData
|
||||
frame.Kind = can.CanDataFrame
|
||||
|
||||
ts = time.Unix(unixSeconds, unixMicros)
|
||||
ts = time.Unix(unixSeconds, unixMicros * int64(time.Microsecond))
|
||||
|
||||
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
|
||||
// skylab busevents
|
||||
// BusParserFunc is a function that takes a string and returns a busevent.
|
||||
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 {
|
||||
return func(s string) (skylab.BusEvent, error) {
|
||||
var b = skylab.BusEvent{}
|
||||
|
|
|
@ -19,13 +19,6 @@ func Test_parseCanDumpLine(t *testing.T) {
|
|||
wantTs time.Time
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "test garbage",
|
||||
args: args{dumpLine: "hosireoie"},
|
||||
wantFrame: can.Frame{},
|
||||
wantTs: time.Unix(0,0),
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "test normal data",
|
||||
args: args{dumpLine: "(1684538768.521889) can0 200#8D643546"},
|
||||
|
@ -34,40 +27,16 @@ func Test_parseCanDumpLine(t *testing.T) {
|
|||
Data: []byte{0x8d, 0x64, 0x35, 0x46},
|
||||
Kind: can.CanDataFrame,
|
||||
},
|
||||
wantTs: time.Unix(1684538768, 521889),
|
||||
wantTs: time.Unix(1684538768, 521889 * int64(time.Microsecond)),
|
||||
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
|
||||
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
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)
|
||||
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