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)