from pyqtgraph.Qt import QtGui, QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import pyqtgraph as pg
import time
import numpy as np
import serial
import threading
import queue
import sys
from more_itertools import split_after

#globalni promenne pro ovladani chodu programu
toggle_switch = False
index_nulling = False
closeEventButton = False
com_port_number = 0

#vlakno DAQworker obsluhuje cteni dat ze serial portu
class DAQworker(threading.Thread):
    def __init__(self, q):
        super().__init__()
        self.q = q #fronta na predavani dat mezi vlakny DAQworker a DynamicPlotter

    # cteni konfiguracniho .txt souboru
    def getConfigFile(self):
        global com_port_number
        fileObject = open('config.txt') #otevreni .txt souboru
        lines = fileObject.readlines()  #precteni radku ze souboru
        self.string_port = lines[0].replace("\n","") #SERIAL COM PORT
        com_port_number = lines[0].replace("\n","")
        self.int_baudrate = int(lines[1]) #BAUDRATE
        fileObject.close() #uzavreni souboru po ukonceni cteni

    # funkce se smyckou While pro cteni COM portu
    def run(self):
        global toggle_switch
        global index_nulling
        global closeEventButton
        DAQworker.getConfigFile(self)
        raw = serial.Serial(self.string_port, timeout=None, baudrate=self.int_baudrate, bytesize=serial.EIGHTBITS,
                                 parity=serial.PARITY_NONE) #konfigurace serioveho portu

        index_data = 0  #start indexu namerenych hodnot
        index_DH = 3    #index DataHigh v comm packetu
        index_DL = 4    #index DataLow v comm packetu
        rozliseni = 5/(2**16)
        raw.close()
        raw.open()

        # smycka while pro precteni poctu bajtu ze seriove linky, roztrizeni dat z komunikacniho protokolu
        # a zapsani do predavaci queue pro vykresleni
        while True:
            if closeEventButton:
                raw.close()
                self._stop()
            if toggle_switch:
                precteno = raw.read(500)
                originalList = list(precteno)
                self.splitted = [x for x in split_after(originalList, lambda x: x == 36) if len(x) == 6]  # tento radek vytvori list listu s namerenymi hodnotami

                for s in self.splitted:
                    calculatedNumber =  s[index_DH] * 256 + s[index_DL]

                    if calculatedNumber > 65535/2:      #we have to determine, which direction the strain gauge is stressed
                        calculatedNumber -= 65536

                    index_data += 1
                    u_volts_bridge = calculatedNumber * rozliseni * (2 / 340.33) * 1000/2.5 #prepocet na mikrovolty z mustku

                    if index_nulling:
                        raw.reset_input_buffer()
                        raw.reset_output_buffer()
                        index_data = 0
                        calculatedNumber = 0
                        u_volts_bridge = 0
                        self.splitted = [0]
                        self.q.put([index_data, u_volts_bridge])
                        index_nulling = False
                    else:
                        self.q.put([index_data,u_volts_bridge])  # obtained and decoded serial data are pushed into queue
# trida obsluhujici vykreslovani GUI a  prevzatych dat od DAQworker vlakna
class DynamicPlotter(QMainWindow):
    def __init__(self):
        super(DynamicPlotter, self).__init__()
        global toggle_switch
        global index_nulling
        global closeEventButton

        #queue from worker to dynamic plotter
        self.q = queue.Queue() #inicializace rady pro komunikaci mezi vlakny
        self.worker = DAQworker(self.q) #inicializace vlakna worker pro cteni, predali jsme mu q

        #my variables
        self.start_time = time.time()
        self.start_flag = 0
        self.numbersList = []   #list dat
        self.indexList = []     #list indexu dat
        self.offset = 0
        self.timestampList = [] #list timestampu
        self.view_window = 3000 #time window of data acquisition plot

        #GUI stuff I
        main_frame = QWidget()
        self.setCentralWidget(main_frame)
        layout = QGridLayout()
        self.start = QPushButton("START")
        self.stop = QPushButton("STOP")
        self.reset = QPushButton("RESET")
        self.plt = pg.PlotWidget(name = 'Realtime DAQ data plot')
        self.nadpis = QLabel()
        self.autor = QLabel()
        self.offset_text = QLabel()
        self.com_port_text = QLabel()

        #adding widgets into layout, than connect layout with frame
        layout.addWidget(self.nadpis, 1, 1, 1, 3)
        layout.addWidget(self.start, 2, 1)
        layout.addWidget(self.stop, 2, 2)
        layout.addWidget(self.reset, 2, 3)
        layout.addWidget(self.plt, 3, 1, 1, 3)
        layout.addWidget(self.autor, 4, 1)
        layout.addWidget(self.offset_text, 4, 2)
        layout.addWidget(self.com_port_text, 4, 3)
        main_frame.setLayout(layout)

        # GUI stuff II
        self.nadpis.setText("DAQ CONTROL SOFTWARE")
        self.nadpis.setAlignment(Qt.AlignCenter)
        self.nadpis.setFont(QFont("Times", 16))
        self.autor.setText("Kaplan Thomas \nFME BUT - Mechatronics 2018")
        self.offset_text.setText("Offset = ")
        self.com_port_text.setText("COM port = ")


        # GUI action connection
        self.start.clicked.connect(self.start_button_clicked)
        self.stop.clicked.connect(self.stop_button_clicked)
        self.reset.clicked.connect(self.reset_button_clicked)

        # pyqtgraph config
        self.plt.showGrid(x=True, y=True)
        self.plt.setLabel('left', 'bridge output voltage', '[mV/V]')
        self.plt.setLabel('bottom', 'index', '[-]')
        self.plt.setXRange(0, self.view_window)
        self.curve = self.plt.plot([0], [0], pen=(255, 0, 0))


        # QTimer and worker start
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.getdataFromDAQworker)
    def closeEvent(self, event, *args, **kwargs, ):
        global closeEventButton
        closeEventButton = True
        event.accept()
        self.close()

    # obsluha start tlacitka
    def start_button_clicked(self):
        print("start clicked")
        global toggle_switch
        toggle_switch = True
        self.timer.start(1)
        self.curve.clear()

        if self.start_flag == 0:
            self.worker.start()
            self.start_flag = 1
    # obsluha stop tlacitka
    def stop_button_clicked(self):
        global toggle_switch
        toggle_switch = False
        self.timer.stop()
    # obsluha reset tlacitka
    def reset_button_clicked(self):
        global index_nulling
        index_nulling = True
        self.numbersList.clear()
        self.indexList.clear()
        self.BufferList.clear()
        self.offset = 0
        self.curve.clear()
        self.curve.setData(self.indexList, self.numbersList)
        print(self.numbersList, self.indexList, self.BufferList)
        self.com_port_text.setText("COM Port = ")
        self.offset_text.setText("Offset = ")
    # funkce volana kazdou periodu casovace pro cteni dat z vlakna
    def getdataFromDAQworker(self):
        global toggle_switch
        global index_nulling
        global com_port_number

        while toggle_switch:
            try:
                self.BufferList = self.q.get(False)
                self.indexList.append(self.BufferList[0])
                if (len(self.indexList) == 100):
                    self.offset = np.mean(self.numbersList)
                    #print(self.offset)
                self.numbersList.append(self.BufferList[1] - round(self.offset))

            except queue.Empty:
                break
            except Exception as e:
                print(e)

        if (len(self.indexList) > self.view_window) and toggle_switch and (not index_nulling):
            self.plt.setXRange(0, len(self.indexList))
            self.curve.setData(self.indexList, self.numbersList)
            offset_print_string = "Offset = " + str(round(self.offset,2)) + " mV"
            comport_print_string = "COM Port = " + str(com_port_number)
            self.offset_text.setText(offset_print_string)
            self.com_port_text.setText(comport_print_string)

# spusteni aplikace
app = QApplication(sys.argv)
mainwindow = DynamicPlotter()
mainwindow.show()
app.exec_()
