Example #1
0
class MagneticApp(MainWindow):
    app_title = "Magnetic Viewer - {0}"
    TIMEOUT = 100

    def __init__(self, data=None, title=None, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setupUi()

        self.errors_data = 0

        # Start/Stop monitor sensor data
        self.monitor_running = False

        # Start/Stop compensate
        self.compensate = False

        # On/Off logging data
        self.logging_enable = False

        # Search available serial ports
        available_ports = sensor.scan_ports()
        if not available_ports:
            self.status.showMessage("No available ports")
            for btn in self.modeButtonGroup.buttons():
                btn.setDisabled(True)
        else:
            self.portbox.addItems(available_ports)

        # Connecting model to consumers
        self.model = SensorDataModel()
        self.centralWidget().table_view.setModel(self.model)

        # Connecting signal/slot
        self.toolbar_buttons['collection'].clicked.connect(self.on_compensate)
        self.toolbar_buttons['log_on'].clicked.connect(self.turn_logging)
        self.toolbar_buttons['select_path'].clicked.connect(
            self.on_select_path)

        # ...button group
        self.modeButtonGroup.buttonClicked[QAbstractButton].connect(
            self.on_run)
        self.viewButtonGroup.buttonClicked[QAbstractButton].connect(
            self.on_switch_mode)

        # ...comboboxs
        self.spin.valueChanged[int].connect(self.on_set_chart_xinterval)

        # ...models
        self.model.rowsInserted.connect(self.on_model_changed)

    def on_model_changed(self):
        self.counter.setText("Rx: {}".format(self.model.rowCount()))

    def timerEvent(self, QTimerEvent):
        """ Handler timer event, every 100ms"""

        # <1> Get data from sensor
        try:
            data = [
                round(item, 1) for item in sensor.SENSOR_QUEUE.get(timeout=0.5)
            ]
        except queue.Empty:
            self.status.showMessage("No sensor data")

            if self.errors_data <= 10000:
                self.errors_data += 1
                self.errors.setText("Err: {}".format(self.errors_data))
            else:
                self.errors.setText("Err: {}".format(">10000"))
            return

        pid, r, p, h, hy_raw, hx_raw, hz_raw, hy, hx, hz = data

        # <2> Append data to model
        self.model.append_data((r, p, h, hy_raw, hx_raw, hz_raw, hy, hx, hz))

        # <4> Show to data view
        self.data_view.update(r, p, h, hy_raw, hx_raw, hz_raw, hy, hx, hz)

        # <3> Apply correction algorithms
        if self.options['dub z'].checkState():
            hy_raw, hx_raw, hz_raw = to_horizont(hy_raw, hx_raw, hz_raw, r, p)

        if self.options['dub soft-iron'].checkState():
            try:
                heading = self.maxdub.correct_heading(hx, hy)
                fmt_value = '{0:.1f}'
                self.data_view2['heading'].setText(fmt_value.format(heading))
            except AttributeError:
                self.options['dub soft-iron'].setCheckState(False)
                self.status.showMessage("Error! Please, calibrate", 1000)

        # <5> Update plot
        if self.options['update charts'].checkState():
            self.charts['inclinometer'].update_plot(r, p)
            #self.charts['heading'].update_plot(h)
            #self.charts['magnitometer'].update_plot(hy, hx, hz)
            #FIXME: При включения графика девиации увеличивается в разы количество пропущенных сигналов
            #self.charts['deviation'].update_plot(hy, hx)

        # <6> Logging data
        if self.logging_enable:
            time = QtCore.QDateTime.currentDateTime().toString(
                "yyyy-MM-dd hh:mm:ss.zzz")
            path = self.lineedit.text()
            str_data = ",".join((str(x)
                                 for x in (time, hex(pid), r, p, h, hy_raw,
                                           hx_raw, hz_raw, hy, hx, hz)))
            str_data += '\n'
            self.status.showMessage(time)
            with open(path, 'a') as f:
                f.write(str_data)

        # <7> Compensate mode
        if self.compensate:
            self.calibrate.update(data)
            self.progress.setValue(self.calibrate.status())
            time = QtCore.QDateTime.currentDateTime().toString(
                "yyyy-MM-dd hh:mm:ss.zzz")
            path = self.lineedit.text()
            #str_data = ",".join((str(x) for x in (time, hex(pid), r, p, h, hy_raw, hx_raw, hz_raw, hy, hx, hz)))
            str_data = ",".join((str(x) for x in (hy, hx)))
            str_data += '\n'
            with open(path, 'a') as f:
                f.write(str_data)

    def on_run(self, btn):
        name = btn.objectName()
        if name == 'start':
            self.on_start()
        elif name == 'stop':
            self.on_stop()

    def on_start(self):
        if self.monitor_running:
            return

        # Disable widgets
        self.portbox.setDisabled(True)
        self.toolbar_buttons['select_path'].setDisabled(True)
        self.toolbar_buttons['rescan'].setDisabled(True)

        # ...disable action rescan
        self.menus['file'].children()[1].setDisabled(True)

        # Set selectable port to sensor
        port = self.portbox.currentText()
        self.serobj = serobj = serial.Serial(port, timeout=0.1)
        self.sensor = sensor.Sensor(serobj)

        # Run recieve data to single thread
        self.t = threading.Thread(target=self.sensor.run, daemon=False)
        self.t.start()

        # Run timer
        self.timer_recieve = self.startTimer(self.TIMEOUT,
                                             timerType=QtCore.Qt.PreciseTimer)

        self.status.showMessage("Running")
        self.monitor_running = True

    def on_stop(self):
        if not self.monitor_running:
            return

        self.status.showMessage("Stop")

        # Enable widgets
        self.portbox.setEnabled(True)
        self.toolbar_buttons['rescan'].setEnabled(True)

        # ...enable action rescan
        self.menus['file'].children()[1].setEnabled(True)

        # Kill timer
        if self.timer_recieve:
            self.killTimer(self.timer_recieve)
            self.timer_recieve = None

        # terminate thread
        self.sensor.terminate()
        self.t.join(timeout=0.1)

        # Close port
        self.serobj.close()
        del self.sensor

        self.monitor_running = False

    def on_clear(self):
        self.model.reset()
        self.centralWidget().chart_view.clear_area()

    def on_switch_mode(self, btn):
        btn_name = btn.objectName()
        if btn_name == 'chart':
            self.stack.setCurrentIndex(0)
        elif btn_name == 'table':
            self.stack.setCurrentIndex(1)
        elif btn_name == 'compensate':
            self.stack.setCurrentIndex(2)
            import subprocess
            subprocess.run(["python", "magnetic_viewer.py"])
        else:
            self.stack.setCurrentIndex(0)

    def on_compensate(self):
        if not self.monitor_running:
            self.status.showMessage("Please, connect sensor")
            return

        if not self.compensate:
            self.toolbar_buttons['collection'].setText('Stop')

            initial = float(self.data_view['heading'].text())
            self.calibrate = Calibrate(initial)

            self.compensate = True
            self.status.showMessage('Start compensate', 1000)
        else:
            self.toolbar_buttons['collection'].setText('Collection')
            self.progress.setValue(0)

            self.maxdub = self.calibrate.compute()
            del self.calibrate

            self.compensate = False
            self.status.showMessage('Stop compensate', 1000)

    def turn_logging(self):
        """ On/Off logging. If logging ON then bottombar visible """
        self.logging_enable = ~self.logging_enable

        if self.logging_enable:
            self.record_bar.setVisible(True)
        else:
            self.record_bar.setVisible(False)

    def on_set_chart_xinterval(self, interval):
        for chart in self.charts.values():
            chart.set_xmax(interval)

    def on_select_path(self):
        ''' Select path to save log '''
        if not os.path.exists(REPORT_PATH):
            try:
                os.mkdir(REPORT_PATH)
            except OSError:
                self.status.showMessage("Error creating path", 2000)

        fname, _ = QFileDialog.getSaveFileName(self, "Select Path",
                                               REPORT_PATH, "Log (*.log)")

        if fname:
            self.lineedit.setText(fname)

    def closeEvent(self, QCloseEvent):
        self._action_quit()

    def _action_quit(self):
        try:
            self.sensor.terminate()
            self.t.join(timeout=0.1)
        except AttributeError:
            pass
        QtCore.QCoreApplication.exit(0)