diff --git a/Tools/LogAnalyzer/DataflashLog.py b/Tools/LogAnalyzer/DataflashLog.py index 3098955ded..38fc82b219 100644 --- a/Tools/LogAnalyzer/DataflashLog.py +++ b/Tools/LogAnalyzer/DataflashLog.py @@ -6,10 +6,9 @@ # AP_FLAKE8_CLEAN -from __future__ import print_function +from __future__ import print_function, division import bisect -import collections import ctypes import sys @@ -161,9 +160,13 @@ class BinaryFormat(ctypes.LittleEndianStructure): ) def to_class(self): - labels = self.labels.decode('ascii') if self.labels else "" + labels = self.labels.decode(encoding="utf-8") if self.labels else "" members = dict( - NAME=self.name.decode('ascii'), MSG=self.type, SIZE=self.length, labels=labels.split(","), _pack_=True + NAME=self.name.decode(encoding="utf-8"), + MSG=self.type, + SIZE=self.length, + labels=labels.split(","), + _pack_=True, ) if type(self.types[0]) == str: @@ -190,7 +193,7 @@ class BinaryFormat(ctypes.LittleEndianStructure): def get_message_attribute(x): ret = getattr(x, attributename) if str(format) in ['Z', 'n', 'N']: - ret = ret.decode('ascii') + ret = ret.decode(encoding="utf-8") return ret p = property(get_message_attribute) @@ -239,6 +242,7 @@ class Channel(object): '''returns a segment of this data (from startLine to endLine, inclusive) as a new Channel instance''' segment = Channel() segment.dictData = {k: v for k, v in self.dictData.items() if k >= startLine and k <= endLine} + segment.listData = [(k, v) for k, v in self.listData if k >= startLine and k <= endLine] return segment def min(self): @@ -365,6 +369,8 @@ class LogIterator: self.iterators[lineLabel] = (index, lineNumber) return self + __next__ = next + def jump(self, lineNumber): '''jump iterator to specified log line''' self.currentLine = lineNumber @@ -417,16 +423,15 @@ class DataflashLogHelper: else: return 1 - od = collections.OrderedDict(sorted(logdata.modeChanges.items(), key=lambda t: t[0])) + changes = [{"line": k, "modeName": v[0], "modeNum": v[1]} for k, v in sorted(logdata.modeChanges.items())] chunks = [] - for i in range(len(od.keys())): - if od.values()[i][0] == "LOITER": - startLine = od.keys()[i] - endLine = None - if i == len(od.keys()) - 1: + for i in range(len(changes)): + if changes[i]["modeName"] == "LOITER": + startLine = changes[i]["line"] + try: + endLine = changes[i + 1]["line"] + except IndexError: endLine = logdata.lineCount - else: - endLine = od.keys()[i + 1] - 1 chunkTimeSeconds = ( DataflashLogHelper.getTimeAtLine(logdata, endLine) - DataflashLogHelper.getTimeAtLine(logdata, startLine) @@ -711,6 +716,7 @@ class DataflashLog(object): for line in f: lineNumber = lineNumber + 1 numBytes += len(line) + 1 + line = line.decode(encoding="utf-8") try: line = line.strip('\n\r') tokens = line.split(', ') diff --git a/Tools/LogAnalyzer/LogAnalyzer.py b/Tools/LogAnalyzer/LogAnalyzer.py index 70abd21692..748cbe793b 100755 --- a/Tools/LogAnalyzer/LogAnalyzer.py +++ b/Tools/LogAnalyzer/LogAnalyzer.py @@ -24,8 +24,6 @@ from __future__ import print_function import argparse import datetime import glob -import imp -import inspect import os import sys import time @@ -74,16 +72,34 @@ class TestSuite(object): # to prevent one being loaded, move it out of that folder, or set that test's .enable attribute to False dirName = os.path.dirname(os.path.abspath(__file__)) testScripts = glob.glob(dirName + '/tests/*.py') - testClasses = [] + + def getTests(module_path): + import inspect + + tests = [] + module_name = os.path.basename(module_path)[:-3] + if sys.version_info >= (3, 5): + from importlib.util import spec_from_file_location, module_from_spec + + spec = spec_from_file_location(module_name, module_path) + m = module_from_spec(spec) + spec.loader.exec_module(m) + else: + from imp import load_source + + m = load_source(module_name, module_path) + for _, _class in inspect.getmembers(m, inspect.isclass): + if _class.__module__ == m.__name__: + tests.append(_class()) + return tests + for script in testScripts: - m = imp.load_source("m", script) - for name, obj in inspect.getmembers(m, inspect.isclass): - if name not in testClasses and inspect.getsourcefile(obj) == script: - testClasses.append(name) - self.tests.append(obj()) + self.tests.extend(getTests(script)) # and here's an example of explicitly loading a Test class if you wanted to do that - # m = imp.load_source("m", dirName + '/tests/TestBadParams.py') + # spec = importlib.util.spec_from_file_location("m", dirName + "/tests/TestBadParams.py") + # m = importlib.util.module_from_spec(spec) + # spec.loader.exec_module(m) # self.tests.append(m.TestBadParams()) def run(self, logdata, verbose): diff --git a/Tools/LogAnalyzer/UnitTest.py b/Tools/LogAnalyzer/UnitTest.py index 4eccc87952..4f94a2eeec 100755 --- a/Tools/LogAnalyzer/UnitTest.py +++ b/Tools/LogAnalyzer/UnitTest.py @@ -70,7 +70,6 @@ try: 'AHRS_ORIENTATION': 0.0, 'SIMPLE': 0.0, 'RC2_MAX': 1929.0, - 'MNT_RC_RATE': 0.0, 'RC8_FUNCTION': 0.0, 'INS_ACCSCAL_X': 0.992788, 'ACRO_P': 4.5, @@ -220,7 +219,6 @@ try: 'RATE_RLL_IMAX': 4500.0, 'HLD_LAT_P': 1.0, 'AHRS_GPS_MINSATS': 6.0, - 'FLOW_TYPE': 0.0, 'RC8_REV': 1.0, 'SONAR_GAIN': 0.2, 'RC2_TRIM': 1521.0, @@ -315,6 +313,8 @@ try: 'BATT_CURR_PIN': 12.0, 'WPNAV_SPEED_UP': 250.0, 'RC1_TRIM': 1524.0, + "MNT_JSTICK_SPD": 0.0, + "FLOW_ENABLE": 0.0, } assert logdata.messages == {} assert logdata.modeChanges == { @@ -335,7 +335,7 @@ try: assert logdata.channels['CTUN']['CRate'].listData[51] == (421, 31) assert logdata.channels['CTUN']['CRate'].listData[115] == (563, -8) assert int(logdata.filesizeKB) == 307 - assert logdata.durationSecs == 155 + assert abs(logdata.durationSecs - 155.399) < 1e-9 assert logdata.lineCount == 4750 # test LogIterator class