blob: 7449569786bc540d8da52cff3ea0649de897efc5 [file] [log] [blame]
Lee Mracek3e16a862019-01-24 11:15:36 -05001#!/usr/bin/python
Dave Smithacbca192016-03-06 15:27:23 -08002
3import collections
Lee Mracek3e16a862019-01-24 11:15:36 -05004from frc971.analysis.logentry import LogEntry
Dave Smithacbca192016-03-06 15:27:23 -08005
6class Dataset(object):
7 def __init__(self):
8 self.time = []
9 self.data = []
10
11 def Add(self, time, data):
12 self.time.append(time)
13 self.data.append(data)
14
15class CollectingLogReader(object):
16 """
17 Reads log files and collected requested data.
18 """
19 def __init__(self):
20 self.signal = collections.OrderedDict()
21
22 def Add(self, binary, struct_instance_name, *data_search_path):
23 """
24 Specifies a specific piece of data to collect
25
26 Args:
27 binary: str, The name of the executable that generated the log.
28 struct_instance_name: str, The name of the struct instance whose data
29 contents should be collected.
30 data_search_path: [str], The path into the struct of the exact piece of
31 data to collect.
32
33 Returns:
34 None
35 """
36 self.signal[(binary, struct_instance_name, data_search_path)] = Dataset()
37
38 def HandleFile(self, f):
39 """
40 Parses the specified log file.
41
42 Args:
43 f: str, The filename of the log whose data to parse.
44
45 Returns:
46 None
47 """
48 with open(f, 'r') as fd:
49 for line in fd:
Dave Smith41e3b792016-03-07 17:45:50 -080050 try:
51 self.HandleLine(line)
52 except Exception as ex:
53 # It's common for the last line of the file to be malformed.
Austin Schuh3a952f32017-03-05 00:57:39 -080054 print("Ignoring malformed log entry: ", line, ex)
Dave Smithacbca192016-03-06 15:27:23 -080055
56 def HandleLine(self, line):
57 """
58 Parses a line from a log file and adds the data to the plot data.
59
60 Args:
61 line: str, The line from the log file to parse
62
63 Returns:
64 None
65 """
66 pline = LogEntry(line)
Dave Smithacbca192016-03-06 15:27:23 -080067
68 for key in self.signal:
69 value = self.signal[key]
70 binary = key[0]
71 struct_instance_name = key[1]
72 data_search_path = key[2]
Austin Schuh3a952f32017-03-05 00:57:39 -080073 boolean_multiplier = False
74 multiplier = 1.0
Dave Smithacbca192016-03-06 15:27:23 -080075
76 # If the plot definition line ends with a "-b X" where X is a number then
77 # that number gets drawn when the value is True. Zero gets drawn when the
78 # value is False.
79 if len(data_search_path) >= 2 and data_search_path[-2] == '-b':
Austin Schuh3a952f32017-03-05 00:57:39 -080080 multiplier = float(data_search_path[-1])
81 boolean_multiplier = True
82 data_search_path = data_search_path[:-2]
83
84 if len(data_search_path) >= 2 and data_search_path[-2] == '-m':
85 multiplier = float(data_search_path[-1])
Dave Smithacbca192016-03-06 15:27:23 -080086 data_search_path = data_search_path[:-2]
87
88 # Make sure that we're looking at the right binary structure instance.
89 if binary == pline.name:
90 if pline.msg.startswith(struct_instance_name + ': '):
Dave Smithacbca192016-03-06 15:27:23 -080091 # Traverse the structure as specified in `data_search_path`.
92 # This lets the user access very deeply nested structures.
Dave Smith41e3b792016-03-07 17:45:50 -080093 _, _, data = pline.ParseStruct()
Dave Smithacbca192016-03-06 15:27:23 -080094 for path in data_search_path:
95 data = data[path]
96
Austin Schuh3a952f32017-03-05 00:57:39 -080097 if boolean_multiplier:
Dave Smithacbca192016-03-06 15:27:23 -080098 if data == 'T':
Austin Schuh3a952f32017-03-05 00:57:39 -080099 value.Add(pline.time, multiplier)
Dave Smithacbca192016-03-06 15:27:23 -0800100 else:
101 value.Add(pline.time, 0)
102 else:
Austin Schuh3a952f32017-03-05 00:57:39 -0800103 value.Add(pline.time, float(data) * multiplier)