4 from PyQt5
import QtCore
5 from PyQt5.QtWidgets
import QWidget, QDialog, QApplication, QPushButton, QVBoxLayout, QLineEdit, QTreeView, QFileSystemModel,\
6 QHBoxLayout, QGridLayout, QMainWindow, QSizePolicy, QSpacerItem, QFileDialog, QMessageBox, QLabel, QRadioButton,\
7 QAbstractItemView, QMenu, QTableWidget,QTableWidgetItem, QSpinBox, QSpacerItem
8 from PyQt5.QtGui
import QMovie, QPicture, QIcon, QDropEvent, QPixmap, QImage
9 from PyQt5.Qt
import QApplication, QClipboard, QStyle
13 from matplotlib.backends.backend_qt5agg
import FigureCanvasQTAgg
as FigureCanvas
14 from matplotlib.backends.backend_qt5agg
import NavigationToolbar2QT
as NavigationToolbar
15 import matplotlib.pyplot
as plt
17 from threading
import Thread
19 from logReader
import Log
20 from logPlotter
import logPlot
26 sys.path.append(
'../supernpp/')
27 sys.path.append(
'../ci_hdw/')
28 from pylib.data_sets
import *
34 START_MODE_FACTORY = 2
37 if sys.platform ==
'darwin':
38 subprocess.check_call([
'open',
'--', path])
39 elif sys.platform ==
'linux':
40 subprocess.check_call([
'xdg-open', path])
41 elif sys.platform ==
'win32':
43 subprocess.Popen(
r'explorer '+path)
48 for fname
in os.listdir(path):
49 if fname.endswith(
'.dat'):
51 if fname.endswith(
'.sdat'):
54 for fname
in os.listdir(path):
55 fpath = os.path.join(path, fname)
56 if os.path.isdir( fpath ):
57 if fname ==
'post_processed':
63 if containsDAT
or containsSDAT:
64 if fname.endswith(
'.csv'):
65 print(
'Deleting: ' + fpath)
69 if fname.endswith(
'.sdat'):
70 print(
'Deleting: ' + fpath)
73 print(
'Finished Cleaning!')
76 print(
'Removing Directory: ' + fpath)
81 settings_filename = os.path.expanduser(
"~") +
'/Documents/Inertial_Sense/dataInformation.json' 83 if settings_filename
is not None:
89 data[
'dataInfo'][
'dataDirectory'] = os.path.dirname(path).replace(
'\\',
'/')
90 data[
'dataInfo'][
'subDirectories'] = [os.path.basename(path)]
92 for root, dirs, files
in os.walk(path):
93 for filename
in files:
94 if "LOG_SN" in filename:
95 serialnum = filename[4:11]
96 if serialnum
not in serialnumbers:
97 serialnumbers += [serialnum]
99 data[
'processData'] = {}
100 data[
'processData'][
'datasets'] = [{}]
101 data[
'processData'][
'datasets'][0][
'SerialNumbers'] = serialnumbers
102 data[
'processData'][
'datasets'][0][
'folder'] = os.path.basename(path)
103 data[
'processData'][
'datasets'][0][
'logType'] =
'DAT' 104 if startMode == START_MODE_HOT:
105 data[
'processData'][
'datasets'][0][
'startMode'] =
'HOT' 106 elif startMode == START_MODE_COLD:
107 data[
'processData'][
'datasets'][0][
'startMode'] =
'COLD' 109 data[
'processData'][
'datasets'][0][
'startMode'] =
'FACTORY' 112 with open(settings_filename,
'w')
as f:
113 json.dump(data, f, indent=4)
116 return str(array[0]) +
'.' + str(array[1]) +
'.' + str(array[2])
119 return str(date[1]+2000) +
'-' + f
'{date[2]:02}' +
'-' + f
'{date[3]:02}' +
' ' + f
'{time[0]:02}' +
':' + f
'{time[1]:02}' +
':' + f
'{time[2]:02}' 124 super(DeviceInfoDialog, self).
__init__(parent)
125 self.setWindowTitle(
"Device Info")
127 if np.shape(log.data[0,DID_DEV_INFO])[0] == 0:
128 self.
label = QLabel(
'No DID_DEV_INFO data available.')
132 self.resize(400, 200)
136 nfields = len(log.data[0, DID_DEV_INFO].dtype.names)
140 self.
table.setColumnCount(9)
141 self.
table.setHorizontalHeaderLabels([
'Serial#',
'Hardware',
'Firmware',
'Build',
'Protocol',
'Repo',
'Build Date',
'Manufacturer',
'AddInfo'])
143 for d, dev
in enumerate(log.data):
144 data = dev[DID_DEV_INFO][0]
145 self.
table.setRowCount(d+1)
146 self.
table.setItem(d, 0, QTableWidgetItem(str(data[1])))
149 self.
table.setItem(d, 3, QTableWidgetItem(str(data[4])))
151 self.
table.setItem(d, 5, QTableWidgetItem(str(data[6])))
153 self.
table.setItem(d, 7, QTableWidgetItem(data[7].decode(
'UTF-8')))
154 self.
table.setItem(d, 8, QTableWidgetItem(data[10].decode(
'UTF-8')))
159 self.resize(2000, 800)
163 super(FlashConfigDialog, self).
__init__(parent)
164 self.setWindowTitle(
"Flash Config")
166 if np.shape(log.data[0,DID_FLASH_CONFIG])[0] == 0:
167 self.
label = QLabel(
'No DID_FLASH_CONFIG data available.')
171 self.resize(400, 200)
175 nfields = len(log.data[0, DID_FLASH_CONFIG].dtype.names)
179 for d, dev
in enumerate(log.data):
181 for f, field
in enumerate(dev[DID_FLASH_CONFIG].dtype.names):
182 if isinstance(dev[DID_FLASH_CONFIG][field][0], np.ndarray):
183 length = len(dev[DID_FLASH_CONFIG][field][0])
184 if d == 0: nfields += length-1
185 for i
in range(length):
186 if d == 0: field_names.append(field +
"[" + str(i) +
"]")
187 vals[d].append(dev[DID_FLASH_CONFIG][field][0][i])
189 if d == 0: field_names.append(field)
190 vals[d].append(dev[DID_FLASH_CONFIG][field][0])
192 self.
table.setRowCount(nfields)
193 self.
table.setColumnCount(log.numDev)
195 self.
table.setHorizontalHeaderLabels([str(ser)
for ser
in log.serials])
196 self.
table.setVerticalHeaderLabels(field_names)
198 hex_fields = [
'ioConfig',
'cBrdConfig',
'RTKCfgBits',
'sysCfgBits']
199 for d
in range(log.numDev):
200 for f, field
in enumerate(field_names):
201 if field
in hex_fields:
202 self.
table.setItem(f, d, QTableWidgetItem(hex(vals[d][f])))
204 self.
table.setItem(f, d, QTableWidgetItem(str(vals[d][f])))
209 self.resize(1280, 900)
216 super(LogInspectorWindow, self).
__init__(parent)
228 self.
config[
'logs_directory'] = os.path.join(os.path.expanduser(
"~"),
"Documents",
"Inertial_Sense",
"Logs")
229 self.
config[
'directory'] =
"" 230 self.
config[
'serials'] = [
"ALL"]
232 yaml.dump(self.
config, file)
246 self.
figure.subplots_adjust(left=0.05, right=0.99, bottom=0.05, top=0.91, wspace=0.2, hspace=0.2)
248 def addButton(self, name, function, multithreaded=True, layout=None):
249 setattr(self, name+
"button", QPushButton(name))
255 getattr(self, name +
"button").clicked.connect(function)
265 layout.addWidget(getattr(self, name +
"button"))
271 if np.shape(self.
log.data[0,DID_DEV_INFO])[0] != 0:
272 info = self.
log.data[0,DID_DEV_INFO][0]
274 self.setWindowTitle(
"LogInspector - " + infoStr)
277 log_dir = config[
'logs_directory']
278 directory = QFileDialog.getExistingDirectory(parent=self, caption=
'Choose Log Directory', directory=log_dir)
283 except Exception
as e:
285 msg.setIcon(QMessageBox.Critical)
286 msg.setText(
"Unable to load log: " + e.__str__())
287 msg.setDetailedText(traceback.format_exc())
291 print(
"loading files from " + directory)
295 print(
"done loading")
311 self.setObjectName(
"LogInspector")
312 self.setWindowTitle(
"LogInspector")
313 self.resize(1280, 900)
314 self.setWindowFlags(self.windowFlags() |
315 QtCore.Qt.WindowSystemMenuHint |
316 QtCore.Qt.WindowMinMaxButtonsHint)
317 self.setWindowIcon(QIcon(
"assets/Magnifying_glass_icon.png"))
331 layout = QHBoxLayout()
334 layout.setStretch(1, 1)
336 widget.setLayout(layout)
337 self.setCentralWidget(widget)
339 self.resize(1450, 1000)
340 self.setAcceptDrops(
True)
347 self.
addButton(
'Pos NED Map',
lambda: self.
plot(
'posNEDMap'))
358 self.
addButton(
'GPS 2 Stats',
lambda: self.
plot(
'gps2Stats'))
359 self.
addButton(
'RTK Pos Stats',
lambda: self.
plot(
'rtkPosStats'))
360 self.
addButton(
'RTK Cmp Stats',
lambda: self.
plot(
'rtkCmpStats'))
367 self.
addButton(
'Magnetometer',
lambda: self.
plot(
'magnetometer'))
398 self.
toolLayout.addItem(QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding))
409 downsampleLabel = QLabel()
410 downsampleLabel.setText(
"DS")
437 QApplication.clipboard().setImage(QImage.fromData(buf.getvalue()))
445 if (e.mimeData().hasUrls()):
446 e.acceptProposedAction()
451 directory = e.mimeData().urls()[0].toLocalFile()
453 except Exception
as e:
458 msg.setIcon(QMessageBox.Critical)
459 msg.setText(
"Unable to load log: " + e.__str__())
460 msg.setDetailedText(traceback.format_exc())
466 self.
dirModel.setFilter(QtCore.QDir.Dirs | QtCore.QDir.NoDotAndDotDot)
474 self.
fileTree.setColumnHidden(1,
True)
475 self.
fileTree.setColumnHidden(2,
True)
476 self.
fileTree.setColumnHidden(3,
True)
479 self.
fileTree.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
480 self.
fileTree.setSelectionMode(QAbstractItemView.SingleSelection)
489 for subdir
in os.listdir(directory):
490 path = os.path.join(directory, subdir)
491 if os.path.isdir(path):
493 elif 'RMS' in subdir:
495 rms_report = f.read()
496 p = re.compile(
r'(?<=^PASS/FAIL).*\n', re.M)
497 line = re.search(p, rms_report).group()
498 failed =
True if "FAIL" in line
else False 509 yaml.dump(self.
config, file)
513 selected_directory = self.
fileTree.model().filePath(self.
fileTree.selectedIndexes()[0])
514 for fname
in os.listdir(selected_directory):
515 if fname.endswith(
'.dat'):
517 self.
load(selected_directory)
518 except Exception
as e:
523 selected_directory = os.path.normpath(self.
fileTree.model().filePath(self.
fileTree.selectedIndexes()[0]))
525 copyAction = menu.addAction(
"Copy path")
526 nppActionHot = menu.addAction(
"Run NPP - Start Hot")
527 nppActionCold = menu.addAction(
"Run NPP - Start Cold")
528 nppActionFactory = menu.addAction(
"Run NPP - Start Factory")
529 setDataInfoDirAction = menu.addAction(
"Set as dataInfo.json directory")
530 openAction = menu.addAction(
"Open folder")
531 cleanFolderAction = menu.addAction(
"Clean folder")
532 deleteFolderAction = menu.addAction(
"Delete folder")
533 action = menu.exec_(self.
fileTree.viewport().mapToGlobal(event))
534 if action == copyAction:
535 cb = QApplication.clipboard()
536 cb.clear(mode=cb.Clipboard )
537 cb.setText(selected_directory, mode=cb.Clipboard)
538 if action == nppActionHot:
541 sys.path.insert(1,
'../../../../python/src')
542 from supernpp.supernpp
import SuperNPP
543 spp = SuperNPP(selected_directory, self.
config[
'serials'])
545 if action == nppActionCold:
548 sys.path.insert(1,
'../../../../python/src')
549 from supernpp.supernpp
import SuperNPP
550 spp = SuperNPP(selected_directory, self.
config[
'serials'], startMode=START_MODE_COLD)
552 if action == nppActionFactory:
555 sys.path.insert(1,
'../../../../python/src')
556 from supernpp.supernpp
import SuperNPP
557 spp = SuperNPP(selected_directory, self.
config[
'serials'], startMode=START_MODE_FACTORY)
559 if action == setDataInfoDirAction:
561 if action == openAction:
563 if action == cleanFolderAction:
565 if action == deleteFolderAction:
566 msg = QMessageBox(self)
567 msg.setIcon(QMessageBox.Question)
568 msg.setText(
"Are you sure you want to delete this folder?\n\n" + selected_directory)
569 msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
571 if result == QMessageBox.Yes:
590 def plot(self, func, args=None):
591 print(
"plotting " + func)
597 if hasattr(self,
'plotter'):
605 print(
"done plotting")
607 if __name__ ==
'__main__':
608 app = QApplication(sys.argv)
609 MainWindow = QMainWindow()
611 configFilePath = os.path.join(os.path.expanduser(
"~"),
"Documents",
"Inertial_Sense",
"config.yaml")
618 if len(sys.argv) > 1:
619 directory = sys.argv[1]
def stopLoadingIndicator(self)
def updateWindowTitle(self)
def showFlashConfig(self)
def handleTreeViewClick(self)
def choose_directory(self)
def populateRMSCheck(self, directory)
def handleTreeDirChange(self)
def dateTimeArrayToString(date, time)
size_t count(InputIterator first, InputIterator last, T const &item)
def openFolderWithFileBrowser(path)
def cleanFolder(path, toplevel=True)
GeneratorWrapper< T > range(T const &start, T const &end, T const &step)
def setDataInformationDirectory(path, startMode=START_MODE_HOT)
def dragEnterEvent(self, e)
def __init__(self, log, parent=None)
def createBottomToolbar(self)
def formatButtonColumn(self)
def load(self, directory)
def __init__(self, configFilePath, parent=None)
def __init__(self, log, parent=None)
def handleTreeViewRightClick(self, event)
def copyPlotToClipboard(self)
def addButton(self, name, function, multithreaded=True, layout=None)
def removeDirectory(fpath)
def plot(self, func, args=None)
def startLoadingIndicator(self)
def verArrayToString(array)
def createButtonColumn(self)
def changeDownSample(self, val)