from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QLabel, QLineEdit, QVBoxLayout, QMessageBox, QCheckBox, QSpinBox, QComboBox, QListWidget, QDialog, QFileDialog, QProgressBar, QTableWidget, QTableWidgetItem
from PyQt5.QtGui import QPalette, QPixmap
from PyQt5.QtCore import Qt
from PyQt5 import QtTest
import os
import sys
import pyqtgraph as pg
from pyqtgraph.dockarea import DockArea, Dock
from Detector_Widget import Detector_Widget
from epics import caput, caget, camonitor, camonitor_clear
from Setup import Setup
from multiprocessing import Process
import fabio as fb
import time
from numpy import *
from scanner import Scanner
from Data_Reducer import Data_Reducer

class Data_Collector(QWidget):
    """
    This class is developed to collect SAXS/WAXS data using different Area detectors with different experimental conditions
    """
    def __init__(self,parent=None):
        QWidget.__init__(self,parent)
        self.palette=QPalette()
        self.cwd=os.getcwd()
        self.setup=Setup(os.path.join(self.cwd,'SetupData'))
        self.detectors=self.setup.detectors
        self.motors=self.setup.motors
        self.scalers=self.setup.scalers
        self.BLParams=self.setup.BLParams

        self.pdIn=False
        self.beamIn=False
        self.mirrorIn=False
        self.align=True
        self.delay=0.1
        self.detPV='None'
        self.palette.setColor(QPalette.Foreground,Qt.green)
        self.undulatorStatus='Idle'
        self.monochromatorStatus='Idle'
        self.pixmapON=QPixmap('./Images/ShutterON.png')
        self.pixmapOFF=QPixmap('./Images/ShutterOFF.png')

        self.experimentFolder=None

        self.vblayout=QVBoxLayout(self)
        self.mainDock=DockArea(self,parent)
        self.vblayout.addWidget(self.mainDock)

        self.beamlineInfoDock=Dock('Beamline Info Dock',size=(1,1))
        self.dataColDock=Dock('Data Collection Dock',size=(1,8))
        self.dataRedDock=Dock('Data reduction Dock',size=(1,8))
        self.scanDock=Dock('Scan Dock',size=(1,8))
        self.mainDock.addDock(self.beamlineInfoDock)
        self.mainDock.addDock(self.dataColDock)
        self.mainDock.addDock(self.dataRedDock)
        self.mainDock.moveDock(self.dataColDock,'above',self.dataRedDock)

        self.create_beamlineInfoDock()
        self.create_dataColDock()
        self.create_dataRedDock()
        self.detectorDialogs={}
        self.detectorWidgets={}
        self.usedDetectors=[]
        self.sampleFolders={}
        self.detectorFolders={}
        self.experimentIsSet=False
        self.darkImage=False
        self.expTime=1.0
        self.sleepTime=0.0
        self.frameCount=1
        camonitor(self.scalers['scaler_start']['PV'],callback=self.changeCountingState)
        camonitor(self.motors['absorber']['PV'],callback=self.monitorAbsorber)
        camonitor(self.motors['shutter']['PV'],callback=self.monitorShutter)
        if self.autoShutterCheckBox.checkState()>0:
            if caget(self.motors['shutter']['PV'])==0:
                ans=QMessageBox.question(self,'Shutter status','The shutter is open. Do you want to close the shutter to proceed further?',QMessageBox.No,QMessageBox.Yes)
                if ans==QMessageBox.Yes:
                    self.shutter_OFF()
        self.undulatorStatusLabel.setPalette(self.palette)
        self.monochromatorStatusLabel.setPalette(self.palette)

    def monitorAbsorber(self,**kwargs):
        """
        monitors the changes in abosorber and accordingly updates the absorber spinbox in the gui
        """
        value=kwargs['value']
        self.absSpinBox.setValue(value)

    def monitorShutter(self,**kwargs):
        """
        monitors the opening and closing of shutter and accordingly show the shutter status in the DataCollector GUI
        """
        value=kwargs['value']
        if value==0:
            self.shutterStatusLabel.setPixmap(self.pixmapON)
            #self.palette.setColor(QPalette.Foreground,Qt.red)
            #self.shutterStatusLabel.setText('Shutter ON')
        else:
            self.shutterStatusLabel.setPixmap(self.pixmapOFF)
            #self.palette.setColor(QPalette.Foreground,Qt.green)
            #self.shutterStatusLabel.setText('Shutter OFF')
        #self.shutterStatusLabel.setPalette(self.palette)

    def create_beamlineInfoDock(self):
        """
        Creates the Beamline information dock
        """
        self.BLDockLayout=pg.LayoutWidget(self)
        #try:
        self.undulatorEnergy=caget(self.BLParams['Undulator_Energy']['PV'])
        self.undulatorGap=caget(self.BLParams['Undulator_Gap']['PV'])
        self.energy=caget(self.BLParams['Energy']['PV']+'RdbkAO')
        self.wavelength= 6.62607e-34*2.9979e8/1.60217e-19/1e3/self.energy*1e10

        undulatorEnergyLabel=QLabel('Undulator Energy')
        self.undulatorEnergyLabel=QLabel('%.5f keV'%(self.undulatorEnergy))
        undulatorGapLabel=QLabel('Undulator Gap')
        self.undulatorGapLabel=QLabel('%.5f mm'%(self.undulatorGap))
        energyLabel=QLabel('Energy')
        undulatorStatusLabel=QLabel('Status')
        self.undulatorStatusLabel=QLabel(self.undulatorStatus)
        monochromatorStatusLabel=QLabel('Status')
        self.monochromatorStatusLabel=QLabel(self.monochromatorStatus)

        self.energyLabel=QLabel('%.5f keV'%self.energy)
        wavelengthLabel=QLabel('Wavelength')
        self.wavelengthLabel=QLabel(u'%.5f \u212B'%self.wavelength)

        row=0
        col=0
        self.BLDockLayout.addWidget(undulatorEnergyLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(self.undulatorEnergyLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(undulatorGapLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(self.undulatorGapLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(undulatorStatusLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(self.undulatorStatusLabel,row=row,col=col)
        col+=1

        row+=1
        col=0
        self.BLDockLayout.addWidget(energyLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(self.energyLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(wavelengthLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(self.wavelengthLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(monochromatorStatusLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(self.monochromatorStatusLabel,row=row,col=col)

        row+=1
        col=0
        JJCVLabel=QLabel('Beamsize V')
        JJCHLabel=QLabel('Beamsize H')
        self.beamsizeV=caget(self.BLParams['JJC_VSize']['PV'])
        self.beamsizeH=caget(self.BLParams['JJC_HSize']['PV'])
        self.JJCVLabel=QLabel('%.3f mm'%self.beamsizeV)
        self.JJCHLabel=QLabel('%.3f mm'%self.beamsizeH)

        self.BLDockLayout.addWidget(JJCVLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(self.JJCVLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(JJCHLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(self.JJCHLabel,row=row,col=col)

        row+=1
        col=0
        JJDVLabel=QLabel('Collimator V')
        JJDHLabel=QLabel('Collimator H')
        self.collsizeV=caget(self.BLParams['JJD_VSize']['PV'])
        self.collsizeH=caget(self.BLParams['JJD_HSize']['PV'])
        self.JJDVLabel=QLabel('%.3f mm'%self.collsizeV)
        self.JJDHLabel=QLabel('%.3f mm'%self.collsizeH)
        self.syncBLParamPushButton=QPushButton('Sync BL Info')
        self.syncBLParamPushButton.clicked.connect(self.sync_BLInfo)
        self.BLDockLayout.addWidget(JJDVLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(self.JJDVLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(JJDHLabel,row=row,col=col)
        col+=1
        self.BLDockLayout.addWidget(self.JJDHLabel,row=row,col=col)
        col+=2
        self.BLDockLayout.addWidget(self.syncBLParamPushButton,row=row,col=col)

        self.beamlineInfoDock.addWidget(self.BLDockLayout)

        self.sync_BLInfo()

        #except:
        #    QMessageBox.warning(self, 'EPICS error','Please check if 15IDA soft-IOC is running or not.',QMessageBox.Ok)
        #    return

    def sync_BLInfo(self):
        """
        Sync all the beamline info
        """
        try:
            camonitor_clear(self.BLParams['Energy']['PV']+'RdbkAO')
            camonitor_clear(self.BLParams['Monochromator_Status']['PV'])
            camonitor_clear(self.BLParams['Undulator_IDStatus']['PV'])
            camonitor_clear(self.BLParams['Undulator_Energy']['PV'])
            camonitor_clear(self.BLParams['Undulator_Gap']['PV'])
            camonitor_clear(self.BLParams['JJC_VSize']['PV'],callback=self.JJCV_changed)
            camonitor_clear(self.BLParams['JJC_HSize']['PV'],callback=self.JJCH_changed)
            camonitor_clear(self.BLParams['JJD_VSize']['PV'],callback=self.JJDV_changed)
            camonitor_clear(self.BLParams['JJD_HSize']['PV'],callback=self.JJDH_changed)
        except:
            pass
        camonitor(self.BLParams['Energy']['PV']+'RdbkAO',callback=self.energyWavelengthChanged)
        camonitor(self.BLParams['Monochromator_Status']['PV'],callback=self.monochromatorStatusCheck)
        camonitor(self.BLParams['Undulator_IDStatus']['PV'],callback=self.undulatorIDStatusCheck)
        camonitor(self.BLParams['Undulator_Energy']['PV'],callback=self.undulatorEnergyStatusCheck)
        camonitor(self.BLParams['Undulator_Gap']['PV'],callback=self.undulatorGapStatusCheck)
        camonitor(self.BLParams['JJC_VSize']['PV'],callback=self.JJCV_changed)
        camonitor(self.BLParams['JJC_HSize']['PV'],callback=self.JJCH_changed)
        camonitor(self.BLParams['JJD_VSize']['PV'],callback=self.JJDV_changed)
        camonitor(self.BLParams['JJD_HSize']['PV'],callback=self.JJDH_changed)

    def monochromatorStatusCheck(self,**kwargs):
        """
        Updates the status of the monochromator
        """
        value=kwargs['value']
        if value==1:
            self.palette.setColor(QPalette.Foreground,Qt.red)
            self.monochromatorStatus='Moving'
        else:
            self.palette.setColor(QPalette.Foreground,Qt.green)
            self.monochromatorStatus='Idle'
        self.monochromatorStatusLabel.setText(self.monochromatorStatus)
        self.monochromatorStatusLabel.setPalette(self.palette)
        pg.QtGui.QApplication.processEvents()

    def undulatorIDStatusCheck(self,**kwargs):
        """
        Updates the status of the Undulator
        """
        value=kwargs['value']
        if value==1:
            self.palette.setColor(QPalette.Foreground,Qt.red)
            self.undulatorStatus='Moving'
        else:
            self.palette.setColor(QPalette.Foreground,Qt.green)
            self.undulatorStatus='Idle'
        self.undulatorStatusLabel.setText(self.undulatorStatus)
        self.undulatorStatusLabel.setPalette(self.palette)

    def undulatorEnergyStatusCheck(self,**kwargs):
        """
        Updates the energy of the undulator
        """
        self.undulatorEnergy=kwargs['value']
        self.undulatorEnergyLabel.setText('%.5f keV'%self.undulatorEnergy)
        #pg.QtGui.QApplication.processEvents()

    def undulatorGapStatusCheck(self,**kwargs):
        """
        Updates the Gap of the undulator
        """
        self.undulatorGap=kwargs['value']
        self.undulatorGapLabel.setText('%.5f mm'%self.undulatorGap)

    def JJCV_changed(self, value=None, **kwargs):
        """
        Updates the JJC Vertical slit sizes in the GUI by sensing the changes in the slit sizes
        """
        try:
            self.beamsizeV= value # caget(self.BLParams['JJC_VSize']['PV'])
            self.JJCVLabel.setText('%.3f mm'%self.beamsizeV)
            self.experimentLogHandle.write('#Beamsize-Vertical : %.3f mm\n'%self.beamsizeV)
        except:
            pass

    def JJCH_changed(self, value=None,**kwargs):
        """
        Updates the JJC Horizontal slit sizes in the GUI by sensing the changes in the slit sizes
        """
        try:
            self.beamsizeH=value#caget(self.BLParams['JJC_HSize']['PV'])
            self.JJCHLabel.setText('%.3f mm'%self.beamsizeH)
            self.experimentLogHandle.write('#Beamsize-Horizontal : %.3f mm\n'%self.beamsizeH)
        except:
            pass

    def JJDV_changed(self, value=None,**kwargs):
        """
        Updates the JJD Vertical slit sizes in the GUI by sensing the changes in the slit sizes
        """
        try:
            self.collsizeV=value#caget(self.BLParams['JJD_VSize']['PV'])
            self.JJDVLabel.setText('%.3f mm'%self.collsizeV)
            self.experimentLogHandle.write('#Collimator-Vertical : %.3f mm\n'%self.collsizeV)
        except:
            pass

    def JJDH_changed(self, value=None,**kwargs):
        """
        Updates the JJD Horizontal slit sizes in the GUI by sensing the changes in the slit sizes
        """
        try:
            self.experimentLogHandle.write('#Collimator-Horizontal : %.3f mm\n'%self.collsizeH)
            self.collsizeH=value#caget(self.BLParams['JJD_HSize']['PV'])
            self.JJDHLabel.setText('%.3f mm'%self.collsizeH)
        except:
            pass

    def energyWavelengthChanged(self,**kwargs):
        """
        Updates the energy in the GUI by sensing the energy change in EPICS
        """
        self.energy=kwargs['value']
        self.energyLabel.setText('%.5f keV'%self.energy)
        self.wavelength= 6.62607e-34*2.9979e8/1.60217e-19/1e3/self.energy*1e10
        self.wavelengthLabel.setText(u'%.5f \u212B'%self.wavelength)
        try:
            self.experimentLogHandle.write('#X-ray Energy : %.5f keV\n'%self.energy)
            self.experimentLogHandle.write('#X-ray Wavelength : %.5f Angs\n'%self.wavelength)
        except:
            pass
        #pg.QtGui.QApplication.processEvents()

    def change_Energy(self,energy):
        """
        Changes the energy in keV value supplied and optimizes the undulator and other optics
        """
        try:
            #caput('15IDA:pid_mono_1.FBON',0) #Switching intensity feedback off
            caput("15IDA:KohzuModeB0.VAL",1) #Putting the monochromator in Automode (1) from Manual mode (0)
            caput(self.BLParams['Energy']['PV']+'AO.VAL',energy)
            while self.monochromatorStatus=='Moving':
                QtTest.QTest.qWait(10)
            caput(self.BLParams['Undulator_Energy']['PV']+'Set.VAL',energy+0.17)
            caput('15ID:Start.VAL',1)
            QtTest.QTest.qWait(self.sleepTime*1000)
            #caput('15IDA:pid_mono_1.FBON',1) #Switching intensity feedback on
        except:
            QMessageBox.warning(self,'Value error','The energy value should be floating point value.',QMessageBox.Ok)

    def create_dataColDock(self):
        """
        Creates the data collection dock
        """
        self.dataColLayout=pg.LayoutWidget(self)
        row=0
        col=0
        self.newExperimentPushButton=QPushButton('New Experiment')
        self.newExperimentPushButton.clicked.connect(self.newExperiment)
        experimentFolderLabel=QLabel('Experimental Folder')

        self.experimentFolderLineEdit=QLineEdit()
        self.openExperimentPushButton=QPushButton('Open Experiment')
        self.openExperimentPushButton.clicked.connect(lambda x: self.openExperiment(expFolder=None))
        self.openScannerPushButton=QPushButton('Open Scanner')
        self.openScannerPushButton.clicked.connect(self.openScanner)
        self.dataColLayout.addWidget(self.newExperimentPushButton,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(experimentFolderLabel,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(self.experimentFolderLineEdit,row=row,col=col,colspan=2)
        col+=2
        self.dataColLayout.addWidget(self.openExperimentPushButton,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(self.openScannerPushButton,row=row,col=col)

        row+=1
        col=0
        detAvailableLabel=QLabel('Detectors Available')
        self.detectorComboBox=QComboBox()
        self.detectorComboBox.addItems(list(self.detectors.keys()))
        self.addDetectorPushButton=QPushButton('Add Detector')
        self.addDetectorPushButton.clicked.connect(lambda x: self.addDetector(detname=None))
        self.showDetectorPushButton=QPushButton('Show Detector')
        self.showDetectorPushButton.clicked.connect(self.showDetector)
        detInUseLabel=QLabel('Detectors in use')
        self.removeDetectorPushButton=QPushButton('Remove Detector')
        self.removeDetectorPushButton.clicked.connect(self.removeDetector)
        self.detectorListWidget=QListWidget()
        self.dataColLayout.addWidget(detAvailableLabel,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(self.detectorComboBox,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(detInUseLabel,row=row,col=col)
        row=row+1
        col=1
        self.dataColLayout.addWidget(self.addDetectorPushButton,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(self.detectorListWidget,row=row,col=col,rowspan=3,colspan=4)
        row+=1
        col=1
        self.dataColLayout.addWidget(self.showDetectorPushButton,row=row,col=col)
        row+=1
        col=1
        self.dataColLayout.addWidget(self.removeDetectorPushButton,row=row,col=col)

        row=row+1
        sampleNameLabel=QLabel('Sample Name')
        self.sampleNameLineEdit=QLineEdit()
        self.sampleNameLineEdit.returnPressed.connect(self.sampleNameChanged)
        sampleImgCountLabel=QLabel('Next Image Number')
        self.sampleImgCounterLabel=QLabel('1')
        self.dataColLayout.addWidget(sampleNameLabel,row=row,col=0)
        self.dataColLayout.addWidget(self.sampleNameLineEdit,row=row,col=1,colspan=3)
        self.dataColLayout.addWidget(sampleImgCountLabel,row=row,col=4)
        self.dataColLayout.addWidget(self.sampleImgCounterLabel,row=row,col=5)

        row=row+1
        self.pdInPositionCheckBox=QCheckBox('PD Position')
        self.pdInPositionCheckBox.setTristate(False)
        self.pdInPositionCheckBox.stateChanged.connect(self.pdInPositionStateChanged)
        self.pdInPositionLineEdit=QLineEdit('-41.5')
        self.beamInPositionCheckBox=QCheckBox('Beam position')
        self.beamInPositionCheckBox.setTristate(False)
        self.beamInPositionCheckBox.stateChanged.connect(self.beamInPositionStateChanged)
        self.beamInPositionLineEdit=QLineEdit('0.0')
        self.mirrorInPositionCheckBox=QCheckBox('Mirror position')
        self.mirrorInPositionCheckBox.setTristate(False)
        self.mirrorInPositionCheckBox.stateChanged.connect(self.mirrorInPositionStateChanged)
        self.mirrorInPositionLineEdit=QLineEdit('-39.0')
        self.enableDisablePDPushButton=QPushButton('Enable')
        self.pdInPositionLineEdit.setDisabled(True)
        self.beamInPositionLineEdit.setDisabled(True)
        self.mirrorInPositionLineEdit.setDisabled(True)
        self.dataColLayout.addWidget(self.pdInPositionCheckBox,row=row,col=0)
        self.dataColLayout.addWidget(self.pdInPositionLineEdit,row=row,col=1)
        self.dataColLayout.addWidget(self.beamInPositionCheckBox,row=row,col=2)
        self.dataColLayout.addWidget(self.beamInPositionLineEdit,row=row,col=3)
        self.dataColLayout.addWidget(self.mirrorInPositionCheckBox,row=row,col=4)
        self.dataColLayout.addWidget(self.mirrorInPositionLineEdit,row=row,col=5)

        row=row+1
        self.pdInButton=QPushButton('Pd in')
        self.beamInButton=QPushButton('Beam in')
        self.mirrorInButton=QPushButton('Mirror in')
        self.pdInButton.clicked.connect(self.bringPDIn)
        self.beamInButton.clicked.connect(self.bringBeamIn)
        self.mirrorInButton.clicked.connect(self.bringMirrorIn)
        self.dataColLayout.addWidget(self.pdInButton,row=row,col=0,colspan=2)
        self.dataColLayout.addWidget(self.beamInButton,row=row,col=2,colspan=2)
        self.dataColLayout.addWidget(self.mirrorInButton,row=row,col=4,colspan=2)

        row=row+1
        col=0
        expTimeLabel=QLabel('Exposure time (s)')
        self.expTimeLineEdit=QLineEdit('1.0')
        frameCountLabel=QLabel('# of Frames')
        self.frameCountLineEdit=QLineEdit('1')
        sleepTimeLabel=QLabel('Sleep time (s)')
        self.sleepTimeLineEdit=QLineEdit('0.0')
        self.expTimeLineEdit.returnPressed.connect(self.expTimeChanged)
        self.frameCountLineEdit.returnPressed.connect(self.frameCountChanged)
        self.sleepTimeLineEdit.returnPressed.connect(self.sleepTimeChanged)
        self.dataColLayout.addWidget(expTimeLabel,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(self.expTimeLineEdit,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(frameCountLabel,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(self.frameCountLineEdit,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(sleepTimeLabel,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(self.sleepTimeLineEdit,row=row,col=col)

        row=row+1
        col=0
        shutterTimeLabel=QLabel('Shutter time')
        self.dataColLayout.addWidget(shutterTimeLabel,row=row,col=col)
        col=col+1
        self.shutterTimeLineEdit=QLineEdit('0.0')
        self.dataColLayout.addWidget(self.shutterTimeLineEdit,row=row,col=col)
        col=col+3
        absLabel=QLabel('Attenuator')
        self.absSpinBox=QSpinBox()
        self.absSpinBox.setRange(0,15)
        self.absSpinBox.setValue(caget(self.motors['absorber']['PV']))
        self.absSpinBox.valueChanged.connect(self.absorberChanged)
        self.absSpinBox.setSingleStep(1)
        self.dataColLayout.addWidget(absLabel,row=row,col=col)
        col=col+1
        self.dataColLayout.addWidget(self.absSpinBox,row=row,col=col)

        row=row+1
        instrumentStatusLabel=QLabel('Instrument status')
        self.instrumentStatus=QLabel()
        measurementStatusLabel=QLabel('Measurement Progress')
        self.measurementProgressDialog=QProgressBar()
        self.dataColLayout.addWidget(instrumentStatusLabel,row=row,col=0)
        self.dataColLayout.addWidget(self.instrumentStatus,row=row,col=1,colspan=3)
        self.dataColLayout.addWidget(measurementStatusLabel,row=row,col=4)
        self.dataColLayout.addWidget(self.measurementProgressDialog,row=row,col=5,colspan=1)

        row=row+1
        self.collectTransmissionCheckBox=QCheckBox('Collect Transmission')
        self.collectTransmissionCheckBox.setTristate(False)
        self.collectDarkCheckBox=QCheckBox('Collect Dark Images')
        self.collectDarkCheckBox.setTristate(False)
        self.collectDarkCheckBox.setChecked(True)
        self.autoShutterCheckBox=QCheckBox('Auto Shutter')
        self.autoShutterCheckBox.setTristate(False)
        self.autoShutterCheckBox.setChecked(True)
        pumpVolLabel=QLabel('Pump Vol (uL)')
        try:
            self.pumpVolLineEdit=QLineEdit(str(caget('15IDD:PHDUltra:TargetVolume_RBV')))
        except:
            self.pumpVolLineEdit=QLineEdit('Pump not Connected')
        self.pumpVolLineEdit.returnPressed.connect(self.targetVolumeChanged)
        self.autoPumpCheckBox=QCheckBox('Auto Pump')
        self.autoPumpCheckBox.setTristate(False)
        self.autoPumpCheckBox.setChecked(False)

        self.dataColLayout.addWidget(self.autoShutterCheckBox,row=row,col=0)
        self.dataColLayout.addWidget(self.collectTransmissionCheckBox,row=row,col=1)
        self.dataColLayout.addWidget(self.collectDarkCheckBox,row=row,col=2)
        self.dataColLayout.addWidget(pumpVolLabel,row=row,col=3)
        self.dataColLayout.addWidget(self.pumpVolLineEdit,row=row,col=4)
        self.dataColLayout.addWidget(self.autoPumpCheckBox,row=row,col=5)

        row=row+1
        pdTransLabel=QLabel('PD Transmission:')
        self.PDTransmissionLabel=QLabel()
        self.PDTransmissionButton=QPushButton('Collect PD Transmission')
        self.PDTransmissionButton.clicked.connect(self.collect_transmission)
        bsTransLabel=QLabel('BS Transmission:')
        self.BSTransmissionLabel=QLabel()
        self.dataColLayout.addWidget(pdTransLabel,row=row,col=0)
        self.dataColLayout.addWidget(self.PDTransmissionLabel,row=row,col=1)
        self.dataColLayout.addWidget(self.PDTransmissionButton,row=row,col=2)
        self.dataColLayout.addWidget(bsTransLabel,row=row,col=3)
        self.dataColLayout.addWidget(self.BSTransmissionLabel,row=row,col=4)

        row=row+1
        positionerLabel=QLabel('Positioner')
        self.positionerComboBox=QComboBox()
        self.positionerComboBox.addItems(list(self.motors.keys()))
        self.addPositionerPushButton=QPushButton('Add Positioner')
        self.removePositionerPushButton=QPushButton('Remove Positioners')
        self.openPositionerFilePushButton=QPushButton('Open Positioner file')
        self.savePositionerFilePushButton=QPushButton('Save Positioner file')
        col=0
        self.dataColLayout.addWidget(positionerLabel,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(self.positionerComboBox,row=row,col=col)
        col+=1
        self.dataColLayout.addWidget(self.addPositionerPushButton,row=row,col=col)
        self.addPositionerPushButton.clicked.connect(self.addPositioner)
        col+=1
        self.dataColLayout.addWidget(self.removePositionerPushButton,row=row,col=col)
        self.removePositionerPushButton.clicked.connect(self.removePositioner)
        col+=1
        self.dataColLayout.addWidget(self.openPositionerFilePushButton,row=row,col=col)
        self.openPositionerFilePushButton.clicked.connect(self.openPositionerFile)
        col+=1
        self.dataColLayout.addWidget(self.savePositionerFilePushButton,row=row,col=col)
        self.savePositionerFilePushButton.clicked.connect(self.savePositionerFile)

        row=row+1
        col=0
        self.positionerTable=QTableWidget()
        self.dataColLayout.addWidget(self.positionerTable,row=row,col=col,colspan=6)
        self.positionerTable.setColumnCount(4)
        self.positionerTable.setHorizontalHeaderLabels(['Positioners','Positoner values','Positioner types','Positioner constraint'])
        self.positionerTable.resizeColumnsToContents()
        row=row+1
        self.collectDarkButton=QPushButton('Collect Dark')
        self.collectDarkButton.clicked.connect(self.collect_dark)
        self.autoReduceCheckBox=QCheckBox('Auto Reduce')
        self.autoReduceCheckBox.setTristate(False)
        self.staticCollectButton=QPushButton('Collect Static')
        self.staticCollectButton.clicked.connect(self.static_collect)
        self.loopSpinBox=QSpinBox()
        self.loopSpinBox.setRange(1,1000)
        self.loopSpinBox.setValue(1)
        self.dynamicCollectButton=QPushButton('Collect Dynamic')
        self.dynamicCollectButton.clicked.connect(self.dynamic_collect)
        self.dataColLayout.addWidget(self.collectDarkButton,row=row,col=0)
        self.dataColLayout.addWidget(self.autoReduceCheckBox,row=row,col=1)
        self.shutterStatusLabel=QLabel('Shutter OFF')
        self.dataColLayout.addWidget(self.shutterStatusLabel,row=row,col=2,colspan=1)
        self.dataColLayout.addWidget(self.staticCollectButton,row=row,col=3,colspan=1)
        self.dataColLayout.addWidget(self.loopSpinBox,row=row,col=4)
        self.dataColLayout.addWidget(self.dynamicCollectButton,row=row,col=5)

        self.dataColDock.addWidget(self.dataColLayout)

    def targetVolumeChanged(self):
        try:
            vol=float(self.pumpVolLineEdit.text())
            try:
                caput('15IDD:PHDUltra:TargetVolume',vol)
            except:
                QMessageBox.warning(self,'Pump error','Please check the pump in connected',QMessageBox.Ok)
        except:
            QMessageBox.warning(self,'Value error','Please provide numerical values only',QMessageBox.Ok)

    def create_dataRedDock(self):
        """
        Create the data reduction Dock for on the fly data reduction from 2D image to azimuthally integrated 1D SAXS data
        """
        self.dataReducer=Data_Reducer(poniFile=None)
        #self.dataReducer.poniFile=None
        self.dataRedDock.addWidget(self.dataReducer)
        self.dataReducer.extractedFolder='/tmp'
        self.dataReducer.extractedFolderLineEdit.setText('/tmp')

    def pdInPositionStateChanged(self):
        """
        Enable disable PD motor settings
        """
        if self.pdInPositionCheckBox.isChecked():
            self.pdInPositionLineEdit.setDisabled(False)
        else:
            self.pdInPositionLineEdit.setDisabled(True)
        pg.QtGui.QApplication.processEvents()

    def beamInPositionStateChanged(self):
        """
        """
        if self.beamInPositionCheckBox.isChecked():
            self.beamInPositionLineEdit.setDisabled(False)
        else:
            self.beamInPositionLineEdit.setDisabled(True)
        pg.QtGui.QApplication.processEvents()

    def mirrorInPositionStateChanged(self):
        """
        """
        if self.mirrorInPositionCheckBox.isChecked():
            self.mirrorInPositionLineEdit.setDisabled(False)
        else:
            self.mirrorInPositionLineEdit.setDisabled(True)
        pg.QtGui.QApplication.processEvents()

    def openPositionerFile(self):
        """
        Open saved positioner file for dynamic measurements
        """
        fname=str(QFileDialog.getOpenFileName(self,'Select a positioner file',self.cwd,("Positioner Files (*.txt)"))[0])
        if fname!='':
            fh=open(fname,'r')
            lines=fh.readlines()
            fh.close()
            try:
                if len(lines)>0:
                    self.positionerTable.clear()
                    self.positionerTable.setHorizontalHeaderLabels(['Positioners','Positoner values','Positioner types','Positioner constraint'])
                    self.positioner={}
                    self.positioner_free={}
                    self.positioner_coupled={}
                    for row in range(self.positionerTable.rowCount()):
                        self.positionerTable.removeRow(0)
                    i=0
                    for line in lines:
                        if line[0]!='#':
                            self.positioner[i]={}
                            row=i
                            self.positionerTable.insertRow(row)
                            txt=line.strip('\n').split()
                            col=0
                            self.positioner[i]['motorName']=txt[0]
                            if txt[0]=='Energy':
                                currVal=caget(self.motors[txt[0]]['PV']+'RdbkAO')
                            elif txt[0]=='Undulator_Energy':
                                currVal=caget(self.motors[txt[0]]['PV'])
                            else:
                                currVal=caget(self.motors[txt[0]]['PV']+'.RBV')
                            self.positionerTable.setItem(row,col,QTableWidgetItem(txt[0]))
                            col+=1
                            self.positioner[i]['valueText']=txt[1]
                            self.positionerTable.setItem(row,col,QTableWidgetItem(txt[1]))
                            values=eval(txt[1])
                            print(values)
                            if type(values)==list:
                                self.positioner[i]['values']=array(values)
                            else:
                                self.positioner[i]['values']=values
                            col+=1
                            self.positioner[i]['valueType']=txt[2]
                            self.positionerTable.setItem(row,col,QTableWidgetItem(txt[2]))
                            if self.positioner[i]['valueType']=='relative':
                                self.positioner[i]['values']=self.positioner[i]['values']+currVal
                            col+=1
                            self.positioner[i]['constraint']=txt[3]
                            self.positionerTable.setItem(row,col,QTableWidgetItem(txt[3]))
                            if txt[3]=='free':
                                self.positioner_free[txt[0]]=self.positioner[i]['values']
                            else:
                                self.positioner_coupled[txt[0]]=self.positioner[i]['values']
                            i+=1
                    self.positionerTable.resizeColumnsToContents()
                    if not self.check_coupledPositioner():
                        QMessageBox.warning(self,'Postioner error','The numper of points of all the coupled positioners should be same. Please check the coupled positioners before starting any dynamic measurement.',QMessageBox.Ok)
                else:
                    QMessageBox.warning(self,'File error','It seems there are there are no lines to read from the file.',QMessageBox.Ok)
            except:
                QMessageBox.warning(self,'File error','The file is not a valid positioner file.',QMessageBox.Ok)

    def check_coupledPositioner(self):
        """
        check the number of points on all the coupled positioner and returns True if all the number of points are same and return False if any two of the coupled positioner have different number of points
        """
        if len(list(set([len(self.positioner_coupled[key]) for key in self.positioner_coupled.keys()])))>1:
            return False
        else:
            return True

    def create_measurementList(self):
        """
        Creates a measurement List by reading the values from the postionerTable the
        """
        coupled_len=len(self.positioner_coupled[list(self.positioner_coupled.keys())[0]])
        self.measurementList={}
        for keyc in self.positioner_coupled.keys():
            self.measurementList[keyc]=[]
        self.measurementCount=0
        if len(self.positioner_free.keys())!=0:
            for keyf in self.positioner_free.keys():
                self.measurementList[keyf]=[]
                for value in self.positioner_free[keyf]:
                    for i in range(coupled_len):
                        for keyc in self.positioner_coupled.keys():
                            self.measurementList[keyf].append(value)
                            self.measurementList[keyc].append(self.positioner_coupled[keyc][i])
                        self.measurementCount+=1
        else:
            for i in range(coupled_len):
                for keyc in self.positioner_coupled.keys():
                    self.measurementList[keyc].append(self.positioner_coupled[keyc][i])
                self.measurementCount+=1

        QMessageBox.information(self,'Measurement Information','Total number of measurements to be done: %d'%self.measurementCount,QMessageBox.Ok)

    def check_motorLimits(self):
        """
        Check the limits of all the motors involved in the dynamic measurments
        """
        check=[]
        for key in self.measurementList.keys():
            if key!='Energy' and key!='Undulator Energy':
                low=caget(self.motors[key]['PV']+'.LLM')
                high=caget(self.motors[key]['PV']+'.HLM')
                valmin,valmax=amin(self.measurementList[key]), amax(self.measurementList[key])
                if valmin>=low and valmin<high:
                    check.append(True)
                else:
                    check.append(False)
                    print('The positioner values of %s are not within  the limits.'%key)
        if all(check):
            return True
        else:
            return False

    def savePositionerFile(self):
        """
        Saves positioer file values from the positionerTable for future dynamic measurements
        """
        QMessageBox.information(self,"Under development","This is still under development. Check back later. Thank you!",QMessageBox.Ok)

    def addPositioner(self):
        """
        Add positioner to the postioner table
        """
        QMessageBox.information(self,"Under development","This is still under development. Check back later. Thank you!",QMessageBox.Ok)

    def removePositioner(self):
        """
        Removes selected positoners from the positioner table
        """
        QMessageBox.information(self,"Under development","This is still under development. Check back later. Thank you!",QMessageBox.Ok)

    def addDetector(self,detname=None):
        """
        Adds and opens an Area detector Module and keep it ready for data collection and viewing
        """
        if detname is None:
            detname=str(self.detectorComboBox.currentText())
        if detname not in self.usedDetectors:
            self.detectorWidgets[detname]=Detector_Widget(imgFName='img_'+detname)
            self.detectorWidgets[detname].detectorComboBox.setCurrentIndex(self.detectorWidgets[detname].detectorComboBox.findText(detname))
            if self.experimentFolder is not None:
                self.detectorWidgets[detname].carsImgFolderChanged(imgFolder=self.experimentFolder)
            else:
                QMessageBox.warning(self,'File Error','Please add an experiment folder first!',QMessageBox.Ok)
                return
            self.detectorComboBox.setCurrentIndex(self.detectorComboBox.findText(detname))
            if self.detectorWidgets[detname].connection:
                self.detectorDialogs[detname]=QDialog(self)
                vbLayout=QVBoxLayout(self.detectorDialogs[detname])
                vbLayout.addWidget(self.detectorWidgets[detname])
                self.detectorDialogs[detname].setWindowTitle(detname)
                self.detectorDialogs[detname].setGeometry(810,0,800,1600)
                self.detectorDialogs[detname].show()
                self.detectorListWidget.addItem(self.detectorComboBox.currentText())
                self.usedDetectors.append(detname)
                self.experimentLogHandle.write('##Detector Added on: '+time.asctime()+'\n')
                self.experimentLogHandle.write('#Detectors : '+str(self.usedDetectors)+'\n')
                self.experimentLogHandle.close()
                self.experimentLogHandle=open(self.experimentLogFile,'a')
                self.experimentIsSet=True
                self.expTimeChanged()
            else:
                del self.detectorWidgets[detname]
            if str(self.sampleNameLineEdit.text())!='':
                self.sampleNameChanged()
        else:
            QMessageBox.warning(self,'Detector Error',detname+'  already in use.',QMessageBox.Ok)

    def showDetector(self):
        """
        Opens of the Area detector corresponding to the selected detector in the list of detectors
        """
        if self.detectorListWidget.selectedItems()!=[]:
            for item in self.detectorListWidget.selectedItems():
                detname=str(item.text())
                self.detectorDialogs[detname].show()
        else:
            QMessageBox.warning(self,'Detector Error','Please select a detector in the Detector list to show.',QMessageBox.Ok)

    def removeDetector(self):
        """
        Removes the detector from the program and closes the corresponding detector module
        """
        for item in self.detectorListWidget.selectedItems():
            detname=str(item.text())
            self.detectorListWidget.takeItem(self.detectorListWidget.row(item))
            self.usedDetectors.remove(detname)
            self.detectorDialogs[detname].done(0)

    def newExperiment(self):
        """
        Sets the data collection software for a new experiment in which it opens up a dialog for a new experimental folder
        """
        for row in range(self.detectorListWidget.count()):
            self.detectorListWidget.item(row).setSelected(True)
        self.removeDetector()
        self.experimentFolder=str(QFileDialog.getExistingDirectory(self,caption='Open new experiment folder',directory='/home/epics/CARS5/Data/Data/saxs'))
        self.experimentFolderLineEdit.setText(self.experimentFolder)
        self.experimentLogFile=os.path.join(self.experimentFolder,'experiment.log')
        if os.path.exists(self.experimentLogFile):
            ans=QMessageBox.question(self,'Experiment warning','The experiment folder already exists. Do you want to append data to the folder?',QMessageBox.No, QMessageBox.Yes)
            if ans==QMessageBox.Yes:
                self.openExperiment(expFolder=self.experimentFolder)
            else:
                self.newExperiment()
        else:
            self.experimentLogHandle=open(self.experimentLogFile,'a')
            self.experimentLogHandle.write('##Experiment started on: '+time.asctime()+'\n')
            self.energyWavelengthChanged(value=self.energy)
            self.JJCH_changed()
            self.JJCV_changed()
            self.JJDV_changed()
            self.JJDH_changed()
            self.experimentLogHandle.close()
            self.experimentLogHandle=open(self.experimentLogFile,'a')
        self.experimentIsSet=True
        self.sampleNameLineEdit.clear()

    def openExperiment(self,expFolder=None):
        """
        Sets up the data collection software to continue with an old experiment which opens up a dialog to select the experimental folder.
        This also imports all the detector used for the old experiment.
        """
        for row in range(self.detectorListWidget.count()):
            self.detectorListWidget.item(row).setSelected(True)
        self.removeDetector()
        if expFolder is not None:
            self.experimentFolder=expFolder
        else:
            self.experimentFolder=str(QFileDialog.getExistingDirectory(self,caption='Open existing experiment folder',directory='/home/epics/CARS5/Data/chemmat/Data/saxs'))
            self.experimentFolderLineEdit.setText(self.experimentFolder)
        self.experimentLogFile=os.path.join(self.experimentFolder,'experiment.log')

        #This is to read the required details from the old file like detector information used in the experiment
        try:
            self.experimentLogHandle=open(self.experimentLogFile,'r')
        except:
            QMessageBox.warning(self,'File Error','It looks like you are doing a new experiment. Please use New Experiment.',QMessageBox.Ok)
            return
        lines=self.experimentLogHandle.readlines()
        self.experimentLogHandle.close()
        self.experimentLogHandle=open(self.experimentLogFile,'a')
        self.experimentLogHandle.write('##Experiment folder accessed on: '+time.asctime()+'\n')
        self.energyWavelengthChanged(value=self.energy)
        self.JJCH_changed()
        self.JJCV_changed()
        self.JJDH_changed()
        self.JJDV_changed()
        self.experimentLogHandle.close()
        self.experimentLogHandle=open(self.experimentLogFile,'a')
        for line in lines:
            if '#Detectors :' in line:
                detnames=line.split(':')[1].strip().lstrip('[').rstrip(']').split(',')

        try:
            for detname in detnames:
                self.addDetector(detname=detname.lstrip('\'').rstrip('\''))
            self.experimentLogHandle.close()
            #This is to open the old file to append the log file with new experimental information
            self.experimentLogHandle=open(self.experimentLogFile,'a')
            self.experimentIsSet=True
        except:
            QMessageBox.warning(self,'Detector Error','No detector found in the loaded experimental settings. Add atleast one detector to use.',QMessageBox.Ok)
        self.sampleNameLineEdit.clear()

        #except:
        #    QMessageBox.warning(self,'Detector warning','No detectors found in the loaded experiment. Please add a detector to continue with measurement.',QMessageBox.Ok)

    def openScanner(self):
        """
        Sets up and opens the scanner for scanning purposes
        """
        if self.experimentIsSet:
            self.mainDock.addDock(self.scanDock)
            self.mainDock.moveDock(self.scanDock,'above',self.dataColDock)
            self.scanFolder=os.path.join(self.experimentFolder,'Scans')
            if not os.path.exists(self.scanFolder):
                os.makedirs(self.scanFolder)
            if not hasattr(self,'scanWidget'):
                self.scanWidget=Scanner(self.scanFolder)
                self.scanDock.addWidget(self.scanWidget)
            else:
                self.scanWidget.changeScanFolder(scanFolder=self.scanFolder)
        else:
            QMessageBox.warning(self,'Experiment warning','Please create or open an experiment first.',QMessageBox.Ok)

    def absorberChanged(self):
        """
        Changes the absorber number used for the measurments
        """
        caput(self.motors['absorber']['PV'],self.absSpinBox.value())

    def expTimeChanged(self):
        """
        Changes the exposure time of the detector
        """
        try:
            self.expTime=float(self.expTimeLineEdit.text())
            for detname in self.usedDetectors:
                self.detectorWidgets[detname].expTimeLineEdit.setText(str(self.expTime))
        except:
            QMessageBox.warning(self,'Value Error','Please input numbers only.\n Setting Exposure time to 1.0 s.',QMessageBox.Ok)
            self.expTime=1.0
            self.expTimeLineEdit.setText(str(self.expTime))

    def frameCountChanged(self):
        """
        Changes the number of frames for each data count
        """
        try:
            self.frameCount=int(self.frameCountLineEdit.text())
        except:
            QMessageBox.warning(self,'Value Error','Please input numbers only.\n Setting # of frames to 1.',QMessageBox.Ok)
            self.frameCount=1
            self.frameCountLineEdit.setText(str(self.frameCount))

    def sleepTimeChanged(self):
        """
        Changes the sleep time of the detector
        """
        try:
            self.sleepTime=float(self.sleepTimeLineEdit.text())
        except:
            QMessageBox.warning(self,'Value Error','Please input numbers only.\n Setting sleep time to 0.0 s.',QMessageBox.Ok)
            self.sleepTime=0.0
            self.sleepTimeLineEdit.setText(str(self.sleepTime))

    def sampleNameChanged(self):
        """
        Changes the sample name and create a new folder with the same name as the sample within the experimental folder. If the sample name exists it just updates the image counter to the latest available counter number
        """
        if self.experimentIsSet and self.detectorListWidget.count()>0:
            if str(self.sampleNameLineEdit.text())!='':
                self.sampleName=str(self.sampleNameLineEdit.text())
                self.sampleFolder=os.path.join(self.experimentFolder,self.sampleName)
                if os.path.exists(self.sampleFolder):
                    ans=QMessageBox.question(self,'Sample exists','The Sample name and the corresponding data folder already exists!\n Appending data in the same folder with different file number.',QMessageBox.No,QMessageBox.Yes)
                    if ans==QMessageBox.Yes:
                        self.read_count_record()
                        self.sampleImgCounterLabel.setText(str(self.sampleCounter))
                        self.experimentLogHandle.write('##Sample folder added/accessed %s\n'%self.sampleFolder)
                    else:
                        self.sampleImgCounterLabel.setText('1')
                        self.sampleNameLineEdit.clear()
                else:
                    os.makedirs(self.sampleFolder)
                    self.experimentLogHandle.write('##Sample folder added/accessed %s\n'%self.sampleFolder)
                    self.sampleCounter=1
                    self.sampleImgCounterLabel.setText('1')
                    self.update_counter_record()
                for icount in range(self.detectorListWidget.count()):
                    detname=str(self.detectorListWidget.item(icount).text())
                    self.detectorFolders[detname]=os.path.join(self.sampleFolder,detname.lower())
                    if not os.path.exists(self.detectorFolders[detname]):
                        os.makedirs(self.detectorFolders[detname])
            else:
                QMessageBox.warning(self,'Name error', 'Please provide a sample name before starting data collection.',QMessageBox.Ok)
        else:
            self.sampleNameLineEdit.clear()
            QMessageBox.warning(self,'Experimental settings warning','Please start a new experiment and add a detector for SAXS/ASAXS measurement',QMessageBox.Ok)

    def update_counter_record(self):
        """
        Updates the .counter_record file
        """
        fh=open(os.path.join(self.sampleFolder,'.count_record'),'w')
        fh.write(str(self.sampleCounter))
        fh.close()

    def read_count_record(self):
        """
        Reads .counter_record file if it exists or create a new one
        """
        try:
            fh=open(os.path.join(self.sampleFolder,'.count_record'),'r')
            self.sampleCounter=int(fh.readline())
        except:
            fh=open(os.path.join(self.sampleFolder,'.count_record'),'w')
            fh.write('1')
            self.sampleCounter=1

    def collect_transmission(self):
        """
        Collects the transmission data i.e Monitor and Photodiode counts just after the sample
        """
        self.bringPDIn()
        self.palette.setColor(QPalette.Foreground,Qt.red)
        self.instrumentStatus.setPalette(self.palette)
        self.instrumentStatus.setText('Collecting transmission data! Please wait...')
        caput(self.scalers['scaler_count_time']['PV'],self.expTime)
        self.shutter_ON()
        caput(self.scalers['scaler_start']['PV'],1,wait=False) #Starts counting.
        while caget(self.scalers['scaler_start']['PV'])!=0:
            pg.QtGui.QApplication.processEvents()
        self.shutter_OFF()
        self.trans_diode_counts=caget(self.scalers['diode']['PV'])
        self.trans_monitor_counts=caget(self.scalers['monitor']['PV'])
        self.PDTransmissionLabel.setText('%.5f'%(self.trans_diode_counts*1.0/self.trans_monitor_counts))
        self.palette.setColor(QPalette.Foreground,Qt.green)
        self.instrumentStatus.setPalette(self.palette)
        self.instrumentStatus.setText('Done')
        pg.QtGui.QApplication.processEvents()

    def collect_dark(self):
        """
        Collects dark images for the corresponding count_time of the sample
        """
        self.darkImage=True
        self.collect_data()
        self.darkImage=False

    def collect_data(self):
        """
        Collects data using 2D Detector for the sample
        """
        if str(self.sampleNameLineEdit.text())=='':
            QMessageBox.warning(self,'Sample Name missing', 'Please provide a sample name before doing data collection',QMessageBox.Ok)
        else:
            self.pre_count()
            self.count_em()
            self.post_count()

    def static_collect(self):
        """
        Collects SAXS data depending upon the settings also collects dark current and transmission using photodiode near the sample
        """
        self.shutter_OFF()
        try:
            self.frameCount=int(self.frameCountLineEdit.text())
            self.sleepTime=float(self.sleepTimeLineEdit.text())
            if str(self.staticCollectButton.text())!='Abort':
                self.abort=False
                self.staticCollectButton.setText('Abort')
                self.measurementProgressDialog.setMinimum(0)
                self.measurementProgressDialog.setMaximum(self.frameCount)
                self.measurementProgressDialog.setValue(0)
                if self.collectDarkCheckBox.isChecked():
                    self.collect_dark()
                for i in range(self.frameCount):
                    if self.abort:
                        break
                    self.collect_data()
                    #print(self.dataReducer.poniFile)
                    if self.dataReducer.poniFile is not None and self.autoReduceCheckBox.isChecked():
                        self.dataReducer.reduce_multiple()

                    if self.sleepTime>1e-3:
                        self.palette.setColor(QPalette.Foreground,Qt.red)
                        self.instrumentStatus.setPalette(self.palette)
                        self.instrumentStatus.setText('Sleeping for %s s. Please wait...'%self.sleepTime)
                        QtTest.QTest.qWait(self.sleepTime*1000)
                    self.measurementProgressDialog.setValue(i+1)
                self.palette.setColor(QPalette.Foreground,Qt.green)
                self.instrumentStatus.setPalette(self.palette)
                self.instrumentStatus.setText('Done')
                self.staticCollectButton.setText('Collect Static')
            else:
                ans=QMessageBox.question(self,'Abort','Do you really like to abort the measurement',QMessageBox.Yes,QMessageBox.No)
                if ans==QMessageBox.Yes:
                    self.abort=True
        except:
            QMessageBox.warning(self,'Value Error','Please provide integer frame counts and floating point number sleep time',QMessageBox.Ok)
        caput(self.scalers['scaler_mode']['PV'],1,wait=True) #Setting Scalar to Autocount mode

    def dynamic_collect(self):
        """
        Collects SAXS with changing PV of either motors or some beamline parameters
        """
        self.shutter_OFF()
        self.NLoops=self.loopSpinBox.value()
        #try:
        self.frameCount=int(self.frameCountLineEdit.text())
        self.sleepTime=float(self.sleepTimeLineEdit.text())
        if str(self.dynamicCollectButton.text())!='Abort':
            self.abort=False
            self.create_measurementList()
            limitsOK=self.check_motorLimits()
            if limitsOK:
                self.dynamicCollectButton.setText('Abort')
                self.measurementProgressDialog.setMinimum(0)
                self.measurementProgressDialog.setMaximum(self.measurementCount*self.NLoops*self.frameCount)
                self.measurementProgressDialog.setValue(0)
                firstPosition={}
                for motorname in self.measurementList.keys():
                    if motorname=='Energy':
                        firstPosition[motorname]=caget(self.motors[motorname]['PV']+'RdbkAO')
                    elif motorname=='Undulator_ID15Energy':
                        firstPosition[motorname]=caget(self.motors['Undulator_Energy']['PV'])
                    else:
                        firstPosition[motorname]=caget(self.motors[motorname]['PV']+'.RBV')
                for loop in range(self.NLoops):
                    if self.abort:
                        break
                    #Recording the intial starting positions of all the motors involved in the Dynamic scan

                    #Starting the Dynamic Scan
#                        self.measurementProgressDialog.setMinimum(0)
#                        self.measurementProgressDialog.setMaximum(self.measurementCount)
#                        self.measurementProgressDialog.setValue(0)
                    for i in range(self.measurementCount):
                        if self.abort:
                            break
                        for motorname in self.measurementList.keys():
                            if motorname=='Energy':
                                caput(self.motors[motorname]['PV']+'AO.VAL',self.measurementList[motorname][i],wait=False)
                            elif motorname=='Undulator_ID15Energy':
                                caput(self.motors[motorname]['PV'],self.measurementList[motorname][i],wait=False)
                            else:
                                caput(self.motors[motorname]['PV']+'.VAL',self.measurementList[motorname][i],wait=False)
                        moving=self.checkMotorsMoving()
                        #Checking the movement of all the motors
                        while moving:
                            if self.abort:
                                break
                            self.palette.setColor(QPalette.Foreground,Qt.red)
                            self.instrumentStatus.setPalette(self.palette)
                            self.instrumentStatus.setText('Motors are moving for the next position. Please wait')
                            pg.QtGui.QApplication.processEvents()
                            moving=self.checkMotorsMoving()
                            QtTest.QTest.qWait(0.1*1000)
                        #Counting starts
                        if self.collectDarkCheckBox.isChecked():
                            self.collect_dark()
                        for j in range(self.frameCount):
                            if self.abort:
                                break
                            self.collect_data()
                            if self.dataReducer.poniFile is not None and self.autoReduceCheckBox.isChecked():
                                self.dataReducer.reduce_multiple()
                            if self.sleepTime>1e-3:
                                self.instrumentStatus.setText('Sleeping for %s s. Please wait...'%self.sleepTime)
                                QtTest.QTest.qWait(self.sleepTime*1000)
                            self.measurementProgressDialog.setValue(loop*self.measurementCount*self.frameCount+self.frameCount*i+j+1)

                #Moving back the motors to the staring position

                for motorname in self.measurementList.keys():
                    if motorname=='Energy':
                        caput(self.motors[motorname]['PV']+'AO.VAL',firstPosition[motorname],wait=False)
                    elif motorname=='Undulator_ID15Energy':
                        caput(self.motors[motorname]['PV'],firstPosition[motorname],wait=False)
                    else:
                        caput(self.motors[motorname]['PV']+'.VAL',firstPosition[motorname],wait=False)
                moving=self.checkMotorsMoving()
                while moving:
                    self.palette.setColor(QPalette.Foreground,Qt.red)
                    self.instrumentStatus.setPalette(self.palette)
                    self.instrumentStatus.setText('Motors are moving back to the starting position. Please wait')
                    pg.QtGui.QApplication.processEvents()
                    moving=self.checkMotorsMoving()
                    QtTest.QTest.qWait(0.1*1000)
                self.palette.setColor(QPalette.Foreground,Qt.green)
                self.instrumentStatus.setPalette(self.palette)
                self.measurementProgressDialog.setValue(self.measurementCount*self.NLoops*self.frameCount)
                self.instrumentStatus.setText('Done')
                self.dynamicCollectButton.setText('Collect Dynamic')
            else:
                QMessageBox.warning(self,'Limit error', 'The motor positions supplied for measurements are beyond the limits. Please review your positioner values.',QMessageBox.Ok)
        else:
            ans=QMessageBox.question(self,'Abort','Do you really like to abort the measurement',QMessageBox.Yes,QMessageBox.No)
            if ans==QMessageBox.Yes:
                self.abort=True
        #except:
        #    QMessageBox.warning(self,'Value Error','Please provide integer frame counts and floating point number sleep time',QMessageBox.Ok)
        #caput(self.scalers['scaler_mode']['PV'],1,wait=True) #Setting Scalar to Autocount mode

    def checkMotorsMoving(self):
        """
        Returns true if any of the motors in the measurement list is moving and returns False if all the motrs are static
        """
        result=[]
        for motorname in self.measurementList.keys():
            if motorname !='Energy' and motorname !='Undulator_ID15Energy':
                result.append(caget(self.motors[motorname]['PV']+'.DMOV'))
        if self.monochromatorStatus=='Moving':
            result.append(0)
        else:
            result.append(1)
        if self.undulatorStatus=='Moving':
            result.append(0)
        else:
            result.append(1)
        return not all(result)

    def bringPDIn(self):
        """
        Brings the photodiode into the beam for transmission measurements
        """
        self.pdInPosition=float(self.pdInPositionLineEdit.text())
        #if not self.pdIn:
        self.palette.setColor(QPalette.Foreground,Qt.red)
        self.instrumentStatus.setPalette(self.palette)
        self.instrumentStatus.setText('Bringing photodiode in. Please wait...')
        #print('I m here')
        caput(self.motors['cmir']['PV'],self.pdInPosition,wait=False)
        while caget(self.motors['cmir']['PV']+'.DMOV')==0:
            pg.QtGui.QApplication.processEvents()
        self.palette.setColor(QPalette.Foreground,Qt.green)
        self.instrumentStatus.setText('Done')
        self.instrumentStatus.setPalette(self.palette)

    def bringBeamIn(self):
        """
        Brings the photodiode into the beam for transmission measurements
        """
        self.beamInPosition=float(self.beamInPositionLineEdit.text())
        self.palette.setColor(QPalette.Foreground,Qt.red)
        self.instrumentStatus.setPalette(self.palette)
        self.instrumentStatus.setText('Bringing beam in. Please wait...')
        caput(self.motors['cmir']['PV'],self.beamInPosition,wait=False)
        while caget(self.motors['cmir']['PV']+'.DMOV')==0:
            pg.QtGui.QApplication.processEvents()
        self.palette.setColor(QPalette.Foreground,Qt.green)
        self.instrumentStatus.setText('Done')
        self.instrumentStatus.setPalette(self.palette)

    def bringMirrorIn(self):
        """
        Brings the mirror in to view sample from within the scattering tube.
        """
        self.mirrorInPosition=float(self.mirrorInPositionLineEdit.text())
        #if not self.mirrorIn:
        self.palette.setColor(QPalette.Foreground,Qt.red)
        self.instrumentStatus.setPalette(self.palette)
        self.instrumentStatus.setText('Bringing Mirror in. Please wait...')
        caput(self.motors['cmir']['PV'],self.mirrorInPosition,wait=False)
        while caget(self.motors['cmir']['PV']+'.DMOV')==0:
           pg.QtGui.QApplication.processEvents()
        self.palette.setColor(QPalette.Foreground,Qt.green)
        self.instrumentStatus.setText('Done')
        self.instrumentStatus.setPalette(self.palette)

    def pre_count(self):
        """
        Do all the necessary settings before counting like:
            1) Setting up the count_time for all the detectors and scalars
            2) Collecting transmission if collectTransmissionCheckBox is checked
        """
        camonitor(self.scalers['scaler_start']['PV'],callback=self.changeCountingState)
        self.palette.setColor(QPalette.Foreground,Qt.red)
        self.instrumentStatus.setPalette(self.palette)
        try:
            shutterTime=float(self.shutterTimeLineEdit.text())
        except:
            shutterTime=0.0
            QMessageBox.warning(self,'Value error','Please check the shutter time. It should be a floating point number.',QMessageBox.Ok)
        self.instrumentStatus.setText('Counting...please wait')
        caput(self.scalers['scaler_mode']['PV'],0,wait=True) #Setting the counter to one-shot mode
        caput(self.scalers['scaler_count_time']['PV'],self.expTime+2.0*shutterTime,wait=True)
        for detname in self.usedDetectors:
            caput(self.detectors[detname]['PV']+'AcquireTime',self.expTime+2.0*shutterTime,wait=True)
        if self.collectTransmissionCheckBox.isChecked() and not self.darkImage:
            self.bringPDIn()
            self.collect_transmission()
            self.bringBeamIn()
        else:
            self.bringBeamIn()
            self.trans_diode_counts=0.0
            self.trans_monitor_counts=1.0

    def shutter_ON(self):
        """
        Put the shutter ON
        """
        caput(self.motors['shutter']['PV'],0)

    def shutter_OFF(self):
        """
        Put the shutter OFF
        """
        caput(self.motors['shutter']['PV'],1)

    def count_em(self):
        """
        Triggers all the detectors and scalers for counting
        """
        if not self.darkImage:
            self.shutter_ON()
            shutterTime=float(self.shutterTimeLineEdit.text())
            QtTest.QTest.qWait(shutterTime*1000) #waiting for 0.3 seconds to open the shutter
        else:
            self.shutter_OFF()
        for detname in self.usedDetectors:
            self.detectorWidgets[detname].detStatus='Acquire'
            self.detectorWidgets[detname].detState='Busy'
        self.palette.setColor(QPalette.Foreground,Qt.red)
        self.instrumentStatus.setPalette(self.palette)
        if self.darkImage:
            self.instrumentStatus.setText('Collecting dark images from all the Area Detectors. Please wait...')
        else:
            self.instrumentStatus.setText('Collecting data from all the Area Detectors. Please wait...')
        #self.counting=Truecamonitor(self.scalers['scaler_start']['PV']
        for detname in self.usedDetectors:
            caput(self.detectors[detname]['PV']+'Acquire',1)
        caput(self.scalers['scaler_start']['PV'],1,wait=False)
        QtTest.QTest.qWait(0.1*1000)
        while self.counting:
            pg.QtGui.QApplication.processEvents()
        if self.autoShutterCheckBox.checkState()>0:
            self.shutter_OFF()
        while any([self.detectorWidgets[detname].detStatus=='Acquire' for detname in self.usedDetectors]):
            while any([self.detectorWidgets[detname].detState!='Idle' for detname in self.usedDetectors]):
                pg.QtGui.QApplication.processEvents()
        self.palette.setColor(QPalette.Foreground,Qt.green)
        self.instrumentStatus.setPalette(self.palette)
        self.instrumentStatus.setText('Counting Finished')

    def changeCountingState(self,**kwargs):
        """
        Updates the counting state
        """
        value=kwargs['value']
        if value!=0:
            self.counting=True
        else:
            self.counting=False
        #print(self.counting)

    def post_count(self):
        """
        Do all the necessary steps afer the counting is over i.e
            1) Calculates the transmission
            2) Reads the images and put all the necessary information together to generate an EDF file to store in correct locations
            3) Advance the image counter by 1
        """
        camonitor_clear(self.scalers['scaler_start']['PV'])
        self.palette.setColor(QPalette.Foreground,Qt.red)
        self.instrumentStatus.setPalette(self.palette)
        for detname in self.usedDetectors:
            if detname=='PhotonII':
                imgFile=caget('13PII_1:TIFF1:FullFileName_RBV',as_string=True)
            else:
                imgFile=caget(self.detectors[detname]['PV']+'FullFileName_RBV',as_string=True)
            if self.darkImage:
                fileout=os.path.join(self.detectorFolders[detname],'%s_%04d_dark.edf'%(self.sampleName,self.sampleCounter))
                self.dataReducer.darkFile=fileout
                self.dataReducer.darkFileLineEdit.setText(fileout)
            else:
                fileout=os.path.join(self.detectorFolders[detname],'%s_%04d.edf'%(self.sampleName,self.sampleCounter))
                self.dataReducer.dataFiles=[fileout]
                self.dataReducer.dataFileLineEdit.setText('[\''+fileout+'\']')
            self.dataReducer.extractedFolder=os.path.join(self.detectorFolders[detname],'extracted_pyFAI')
            if not os.path.exists(self.dataReducer.extractedFolder):
                os.makedirs(self.dataReducer.extractedFolder)
            self.dataReducer.extractedFolderLineEdit.setText(self.dataReducer.extractedFolder)
            #self.instrumentStatus.setText('Saving file: %s'%fileout)
            cars_imgFile=imgFile.replace(self.detectors[detname]['det_folder'],self.detectors[detname]['cars_folder'])
            #print(cars_imgFile)
            QtTest.QTest.qWait(2*1000)
            img=fb.open(cars_imgFile)
            file=fb.edfimage.EdfImage()
            file.data=img.data
            file.header=img.header
            self.monitor_counts=caget(self.scalers['monitor']['PV'])
            self.count_time=caget(self.scalers['scaler_count_time']['PV'])
            self.diode_counts=caget(self.scalers['diode']['PV'])
            self.BSdiode_counts=caget(self.scalers['bs_diode']['PV'])
            file.header['Time']=os.path.getctime(cars_imgFile)
            file.header['Monitor']=self.monitor_counts#1000#caget(self.scalers['Monitor']['PV'])
            file.header['count_time']=self.count_time#1.0#caget(self.scalers['count_time']['PV'])
            file.header['Diode']=self.diode_counts#300#caget(self.scalers['Diode']['PV'])
            file.header['BSDiode']=self.BSdiode_counts#300#caget(self.scalers['BSDiode']['PV'])
            file.header['transDiode']=self.trans_diode_counts
            file.header['transMonitor']=self.trans_monitor_counts
            file.header['xcradle']=0.0
            for key in self.motors.keys():
                if key != 'Energy' and key != 'Undulator_ID15Energy' and key != 'Undulator_Energy':
                    file.header[key]=caget(self.motors[key]['PV']+'.RBV')
            file.header['Wavelength']=self.wavelength
            file.header['Energy']=self.energy
            file.write(fileout)
            self.BSTransmissionLabel.setText('%.5f'%(self.BSdiode_counts/self.monitor_counts))
        self.palette.setColor(QPalette.Foreground,Qt.green)
        self.instrumentStatus.setPalette(self.palette)
        self.instrumentStatus.setText('Done')
        if not self.darkImage:
            self.sampleCounter+=1
            self.update_counter_record()
            self.sampleImgCounterLabel.setText(str(self.sampleCounter))
        if self.autoPumpCheckBox.isChecked() and not self.darkImage:
            caput('15IDD:PHDUltra:TargetVolume',float(self.pumpVolLineEdit.text()))
            caput('15IDD:PHDUltra:Infuse',1)
            self.palette.setColor(QPalette.Foreground,Qt.red)
            self.instrumentStatus.setPalette(self.palette)
            t1=time.time()
            QtTest.QTest.qWait(5*1000)
            while caget('15IDD:PHDUltra:PumpState',as_string=True)!='Idle':
                self.instrumentStatus.setText('Now pumping new solution for next frame')
                QtTest.QTest.qWait(0.01*1000)
            t2=time.time()
            print(t2-t1)
            caput("15IDD:PHDUltra:ClearVolume",0)
            caput("15IDD:PHDUltra:Infuse",0)
            self.palette.setColor(QPalette.Foreground,Qt.red)
            self.instrumentStatus.setPalette(self.palette)
            self.instrumentStatus.setText('Done')

if __name__=='__main__':
    app=QApplication(sys.argv)
    w=Data_Collector()
    w.setWindowTitle('Data Collector')
    w.setGeometry(0,0,800,800)

    w.show()
    sys.exit(app.exec_())


import tensorflow as tf
import numpy as np
import os
from rbn import RestrictedBoltzmanMachinesLayer
from tqdm import tqdm
trunc_normal = lambda stddev: tf.truncated_normal_initializer(0.0, stddev)

class DeepBeliefNetwork:
    #include all hyperparameters in init like learning rate, batch size, decay_rate .....
    def __init__(self, pretrain_iterations,rbm_layers,rbm_activations,freeze_rbms,
                 dense_layers,dense_activations,batch_normalization,output_activation,
                 batch_size,learning_rate,beta1,keep_chance=0.5,
                 name="dbn" ):
        #RBM Network
        self.rbms = []
        for i, layer in enumerate(rbm_layers):
            rbm_layer_name = "rbm_"+str(layer) + str(i)
            self.rbms.append(RestrictedBoltzmanMachinesLayer(layer, rbm_layer_name, freeze_rbms[i]))
        self.pretrain_iterations = pretrain_iterations
        self.learning_rbm_rate = learning_rate

        #Dense Network
        self.dense_layers = dense_layers
        self.dense_activation = dense_activations
        self.output_activation = output_activation
        self.batch_normalization = batch_normalization
        self.keep_chance = keep_chance
        self.name = name

        #Hyperparameters
        self.batch_size = batch_size
        self.learning_rate = learning_rate
        self.beta1 = beta1
        self.learning_rate_decay_examples = 4000
        self.learning_rate_decay = 0.96

        self.graph = None

    def pretrain(self,features):
        for i, layer in enumerate(self.rbms):
            best_loss = 121212121212.12
            since_improve = 0
            for j in tqdm(range(self.pretrain_iterations), desc='Pretraining Layer '+str(i+1)+' of '+str(len(self.rbms))):
                output = features[np.random.randint(0,features.shape[0],1)]
                for rbm in self.rbms[:i]:
                    output = rbm.sample_hidden_from_visible(output)
                loss = layer.rbn_update(output, self.learning_rbm_rate)
                if loss < best_loss:
                    best_loss = loss
                    since_improve = 0
                elif since_improve > 200:
                    self.learning_rbm_rate *= 0.96
                    since_improve = 0
                else:
                    since_improve += 1

    def variable_summaries(self,var):
        """Attach a lot of summaries to a Tensor (for TensorBoard visualization)."""
        with tf.name_scope('summaries'):
            mean = tf.reduce_mean(var)
            tf.summary.scalar('mean', mean)
            with tf.name_scope('stddev'):
                stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))
            tf.summary.scalar('stddev', stddev)
            tf.summary.scalar('max', tf.reduce_max(var))
            tf.summary.scalar('min', tf.reduce_min(var))
            tf.summary.histogram('histogram', var)

    def weight_variable(self,shape):
        initial = tf.truncated_normal(shape, stddev=0.01)
        return tf.Variable(initial)

    def bias_variable(sefl,shape): #tf.Variable(tf.zeros([num_units]), name='bias')
        initial = tf.constant(0.1, shape=shape)
        return tf.Variable(initial)

    def rbn_layer(self,input_tensor, weights, bias, act, is_frozen,layer_name="rbn_layer"):
       # print("this",weights)
        with tf.name_scope(layer_name):
            with tf.name_scope('weights_rbn'):
                if is_frozen:
                    with tf.name_scope('frozen_weights'):
                        #weights_var = tf.Variable(initial_value=tf.zeros([weights.shape[0], weights.shape[1]], tf.float32),trainable=False, dtype=tf.float32,validate_shape=False)
                        #weights_new = tf.assign_add(weights_var, weights)
                        #self.variable_summaries(weights_new)
                        weights_new = tf.get_variable(layer_name+"weights_frozen",initializer=weights.astype(np.float32),trainable=False, dtype=tf.float32)
                else:
                    with tf.name_scope('not_frozen_weights'):
                        #weights_var = tf.Variable(initial_value=tf.zeros([weights.shape[0], weights.shape[1]], tf.float32), dtype=tf.float32,validate_shape=False)
                        #weights_new = tf.assign_add(weights_var, weights)
                        #self.variable_summaries(weights_new)
                        weights_new = tf.get_variable(layer_name+"_weights_not_frozen",initializer=weights.astype(np.float32), dtype=tf.float32)
            with tf.name_scope('biases'):
                if is_frozen:
                    with tf.name_scope('frozen_bias'):
                        #biases_var = tf.Variable(initial_value=tf.zeros([weights.shape[1]]),trainable=False, dtype=tf.float32, validate_shape=False)
                        #bias_new = tf.assign_add(biases_var, np.squeeze(bias))
                        #self.variable_summaries(bias_new)
                        bias_new = tf.get_variable(layer_name+"bias_frozen",initializer=bias.astype(np.float32),trainable=False, dtype=tf.float32)
                else:
                     with tf.name_scope('not_frozen_bias'):
                        #biases_var = tf.Variable(initial_value=tf.zeros([weights.shape[1]]), dtype=tf.float32, validate_shape=False)
                        #bias_new = tf.assign_add(biases_var, np.squeeze(bias))
                        #self.variable_summaries(bias_new)
                        bias_new = tf.get_variable(layer_name+"bias_not_frozen",initializer=bias.astype(np.float32), dtype=tf.float32)
            with tf.name_scope('Wx_plus_b'):
                preactivate = tf.matmul(input_tensor, weights_new) + bias_new
                tf.summary.histogram('pre_activations', preactivate)
            activations = act(preactivate, name='activation')
            tf.summary.histogram('activations', activations)
            return activations

    def nn_layer(self,input_tensor, input_dim, output_dim, act=tf.nn.relu,batch_normalization=True,layer_name="nn_layer"):
        with tf.name_scope(layer_name):
            # This Variable will hold the state of the weights for the layer
            with tf.variable_scope('weights'):
                weights = self.weight_variable([input_dim, output_dim])
                self.variable_summaries(weights)
            with tf.variable_scope('biases'):
                #biases = self.bias_variable([output_dim])
                biases = tf.Variable(tf.zeros([output_dim]))
                self.variable_summaries(biases)
            with tf.variable_scope('Wx_plus_b'):
                preactivate = tf.matmul(input_tensor, weights) + biases
                tf.summary.histogram('pre_activations', preactivate)
            if batch_normalization:
                preactivate = tf.layers.batch_normalization(preactivate)
            activations = act(preactivate, name='activation')
            tf.summary.histogram('activations', activations)
            return activations, preactivate

    def model_inputs(self, input_size, output_size):
        with tf.name_scope('placeholders'):
            in_placeholder = tf.placeholder(tf.float32, [None, input_size], name='input')
            out_placeholder = tf.placeholder(tf.float32, [None, output_size], name='labels')
            dropout_placeholder = tf.placeholder(tf.float32, name='dropout')
            return in_placeholder, out_placeholder, dropout_placeholder

    def rbm_network(self,out, rbms):
        with tf.name_scope("rbm"):
            for rbm in rbms:
                    out = self.rbn_layer(input_tensor=out,
                                         weights=rbm.W,
                                         bias=rbm.bias_hidden,
                                         act=tf.nn.sigmoid,
                                         is_frozen=rbm.is_frozen,
                                         layer_name=rbm.name)
            num_prev_outputs = rbms[-1].num_hidden
            return out, num_prev_outputs

    def dense_network(self, out, num_prev_outputs, fully_connected_layers, activation,batch_normalization, dropout):
        with tf.name_scope("dense"):
            for i, connected in enumerate(fully_connected_layers):
                out = tf.layers.dropout(out, dropout, name='dropout_'+str(i))
                out,_ = self.nn_layer(out, num_prev_outputs, connected, act=activation[i],batch_normalization=batch_normalization[i],layer_name="dense_layer_"+str(i))

                num_prev_outputs = connected
            return out, num_prev_outputs

    def last_layer(self, out, num_prev_outputs,output_size, dropout):
        out = tf.layers.dropout(out, dropout, name='dropout_before_last_layer')
        out,net = self.nn_layer(out, num_prev_outputs, output_size, act=tf.nn.softmax,layer_name="OutputLayer")
        return out, net

    def network(self, features,dropout, rbms,fully_connected_layers, activation, output_size,batch_normalization):
        with tf.variable_scope('model_architecture'):
            out_rbm, num_prev_outputs = self.rbm_network(features, rbms)
            out_dense, num_prev_outputs_one = self.dense_network(out_rbm, num_prev_outputs,fully_connected_layers, activation,batch_normalization, dropout)
            out, net = self.last_layer(out_dense, num_prev_outputs_one,output_size, dropout)
            return out, net

    def model_loss_accuracy(self,labels,out, net):
        with tf.name_scope("loss"):
            loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=labels, logits=net))
        with tf.name_scope('accuracy'):
            with tf.name_scope('correct_prediction'):
                correct_prediction = tf.equal(tf.argmax(out, 1),tf.argmax(labels, 1))
            with tf.name_scope('final_accuracy'):
                accuracy = tf.reduce_sum(tf.cast(correct_prediction, tf.float32))
        return loss, accuracy, correct_prediction

    def model_opt(self, loss, learning_rate, beta1):
        t_vars = tf.trainable_variables()
        t_vars_list = [var for var in t_vars if var.name.startswith('model_architecture')]
        train_opt = tf.train.AdamOptimizer(learning_rate, beta1=beta1).minimize(loss, var_list=t_vars_list)
        #train_opt = tf.train.RMSPropOptimizer(learning_rate).minimize(loss, var_list=t_vars_list)
        return train_opt

    def build_graph(self,rbn_data,input_size, output_size):
        self.pretrain(rbn_data)
        if self.graph is None:
            g = tf.Graph()
        else:
            g = self.graph
        with g.as_default():
            self.features, self.labels, self.dropout = self.model_inputs(input_size, output_size)
            self.global_step = tf.Variable(0, name='global_step', trainable=False)
            self.out, self.net = self.network(self.features, self.dropout, self.rbms, self.dense_layers, self.dense_activation, output_size, self.batch_normalization)
            self.loss, self.accuracy, self.correct_prediction = self.model_loss_accuracy(self.labels, self.out, self.net)
            self.exp_learning_rate = tf.train.exponential_decay(self.learning_rate,
                                                    self.global_step * self.batch_size ,
                                                    self.learning_rate_decay_examples,
                                                    self.learning_rate_decay,
                                                    staircase=True)
            self.train_op = self.model_opt(self.loss, self.exp_learning_rate,self.beta1)
        self.graph = g


from conductr_cli import conduct_logging, conduct_info, conduct_url
import json
import requests

@conduct_logging.handle_connection_error
@conduct_logging.handle_http_error
def events(args):
    """`conduct events` command"""

    request_url = conduct_url.url('bundles/{}/events?count={}'.format(args.bundle, args.lines), args)
    response = requests.get(request_url)
    conduct_logging.raise_for_status_inc_3xx(response)

    data = [
        {
            'time': conduct_logging.format_timestamp(event['timestamp'], args),
            'event': event['event'],
            'description': event['description']
        } for event in json.loads(response.text)
    ]
    data.insert(0, {'time': 'TIME', 'event': 'EVENT', 'description': 'DESC'})

    padding = 2
    column_widths = dict(conduct_info.calc_column_widths(data), **{'padding': ' ' * padding})

    for row in data:
        print('''\
{time: <{time_width}}{padding}\
{event: <{event_width}}{padding}\
{description: <{description_width}}{padding}'''.format(**dict(row, **column_widths)))


# Apache Software License 2.0
#
# Copyright (c) ZenML GmbH 2023. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from artifacts.model_metadata import ModelMetadata
from pydantic import BaseConfig
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier

from zenml.config import DockerSettings
from zenml.integrations.constants import (
    AWS,
    EVIDENTLY,
    KUBEFLOW,
    KUBERNETES,
    MLFLOW,
    SKLEARN,
    SLACK,
)
from zenml.model_registries.base_model_registry import ModelVersionStage

PIPELINE_SETTINGS = dict(
    docker=DockerSettings(
        required_integrations=[
            AWS,
            EVIDENTLY,
            KUBEFLOW,
            KUBERNETES,
            MLFLOW,
            SKLEARN,
            SLACK,
        ],
    )
)

DEFAULT_PIPELINE_EXTRAS = dict(notify_on_success=False, notify_on_failure=True)

class MetaConfig(BaseConfig):
    pipeline_name_training = "e2e_use_case_training"
    pipeline_name_batch_inference = "e2e_use_case_batch_inference"
    mlflow_model_name = "e2e_use_case_model"
    target_env = ModelVersionStage.STAGING

    ### ADD YOUR OWN CODE HERE - THIS IS JUST AN EXAMPLE ###
    # This set contains all the models that you want to evaluate
    # during hyperparameter tuning stage.
    model_search_space = {
        ModelMetadata(
            RandomForestClassifier,
            search_grid=dict(
                criterion=["gini", "entropy"],
                max_depth=[2, 4, 6, 8, 10, 12],
                min_samples_leaf=range(1, 10),
                n_estimators=range(50, 500, 25),
            ),
        ),
        ModelMetadata(
            DecisionTreeClassifier,
            search_grid=dict(
                criterion=["gini", "entropy"],
                max_depth=[2, 4, 6, 8, 10, 12],
                min_samples_leaf=range(1, 10),
            ),
        ),
    }


"""
This module is responsible for the creation of all required image resources.

DISCUSSION MIGU: let's use the Python Pillow framework instead. It works with *.png files => no ugly dithering artifacts!
"""

import cards.cardsmodel
import events
import pickle
import resources.paths
#from PIL import Image
import os

from resources import paths

def resize_gifs():
    path_list = os.listdir(paths.PATH_CARD_IMAGES)
    for path in path_list:
        img_in = Image.open(paths.PATH_CARD_IMAGES + path)
        img_size_x = img_in.size[0]
        img_size_y = img_in.size[1]
        fac = 0.5
        img_size_x *= fac
        img_size_y *= fac
        img_in = img_in.resize((int(img_size_x), int(img_size_y)), Image.ANTIALIAS)
        img_out_path = paths.PATH_CARD_IMAGES_SMALL + path
        img_in.save(img_out_path)

# fügt den String als letzte Zeile des Files hinzu
def add_line_to_countries():
    for file in os.listdir(paths.PATH_COUNTRIES):
        if file.endswith(".txt"):
            with open(paths.PATH_COUNTRIES+file, 'a') as mile:
                mile.write('\n'+'times_clicked=0')

    print('done')

def create_dictionary():
    keys = []
    values = []
    for card in cards.cardsmodel.early_card_list: # befüllen der keys mit card namen und values None
        keys.append(card.name)
        values.append(None)

    for card in cards.cardsmodel.mid_card_list: # befüllen der keys mit card namen und values None
        keys.append(card.name)
        values.append(None)

    event_dic = dict(zip(keys, values))
    event_dic['Five Year Plan'] = events.FiveYearPlan()
    event_dic['Duck and Cover'] = events.DuckAndCover()
    event_dic['Marshall Plan'] = events.MarshallPlan()
    event_dic['Captured Nazi Scientist'] = events.CapturedNaziScientist()
    event_dic['Fidel'] = events.Fidel()
    event_dic['Socialist Governments'] = events.SocialistGovernments()
    event_dic['Blockade'] = events.Blockade()
    event_dic['Romanian Abdication'] = events.RomanianAbdication()
    #event_dic['Vietnam Revolts'] = events.SocialistGovernments()
    event_dic['Comecon'] = events.Comecon()
    event_dic['Independent Reds'] = events.IndependentReds()
    event_dic['Truman Doctrine'] = events.TrumanDoctrine()
    event_dic['Korean War'] = events.KoreanWar()
    event_dic['Allende'] = events.Allende()
    event_dic['Colonial Rear Guards'] = events.ColonialRearGuards()
    event_dic['South African Unrest'] = events.SouthAfricaUnrest()
    event_dic['Portuguese Empire Crumbles'] = events.PortugueseEmpireCrumbles()

    event_dic['Alliance for Progress'] = events.AllianceForProgress()
    event_dic['John Paul II Elected Pope'] = events.JohnPaul2ElectedPope()
    event_dic['Liberation Theology'] = events.LiberationTheology()
    event_dic['OAS Founded'] = events.OASFounded()
    event_dic['Panama Canal Returned'] = events.PanamaCanalReturned()
    event_dic['Puppet Governments'] = events.PuppetGovernments()
    event_dic['Sadat Expels Soviets'] = events.SadatExpelsSoviets()
    event_dic['The Voice of America'] = events.TheVoiceOfAmerica()

    print (event_dic)
    with open(resources.paths.PATH_EVENT_DICTIONARY, 'wb') as f: # speichern des fertigen dictionaries
        pickle.dump(event_dic, f, protocol=2)
    print('saved dictionary')


import os
import re
import numpy as np
import tensorflow as tf
from tensorflow.python.platform import gfile
from scipy import misc
import align.detect_face

minsize = 20  # minimum size of face
mtcnn_threshold = [0.6, 0.7, 0.7]  # three steps's threshold
factor = 0.709  # scale factor
gpu_memory_fraction = 1.0
image_size = 160
margin = 32

model_path = os.path.join(os.path.dirname(__file__), '../../data/20170512-110547/20170512-110547.pb')

class FacenetFaceRecognize:
    def __init__(self):
        self.pnet = None
        self.rnet = None
        self.onet = None
        self.create_mtcnn()
        self.inception_resnet_v1 = inception_resnet()

    def create_mtcnn(self):
        print('Creating networks and loading parameters')
        with tf.Graph().as_default():
            gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=gpu_memory_fraction)
            sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False))
            with sess.as_default():
                self.pnet, self.rnet, self.onet = align.detect_face.create_mtcnn(sess, None)

    def detect_face(self, img_arr):
        bounding_boxes, _ = align.detect_face.detect_face(img_arr, minsize, self.pnet, self.rnet, self.onet,
                                                          mtcnn_threshold, factor)

        bounding_boxes = [(left, top, right, bottom) for (left, top, right, bottom, _) in
                          bounding_boxes]
        return bounding_boxes

    def trans_image_emb(self, img, bounding_box):
        cropped = face_align(image_size, img, bounding_box)

        emb = self.inception_resnet_v1([cropped])[0]

        return emb

def inception_resnet():
    with tf.Graph().as_default():
        sess = tf.Session()
        with sess.as_default():
            # Load the model
            load_model(model_path)

            # Get input and output tensors
            images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0")
            embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0")
            phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")

            # Run forward pass to calculate embeddings
            inception_resnet_fun = lambda images: sess.run(embeddings, feed_dict={images_placeholder: images,
                                                                                phase_train_placeholder: False})
            return inception_resnet_fun

def load_model(model):
    # Check if the model is a model directory (containing a metagraph and a checkpoint file)
    #  or if it is a protobuf file with a frozen graph
    model_exp = os.path.expanduser(model)
    if os.path.isfile(model_exp):
        print('Model filename: %s' % model_exp)
        with gfile.FastGFile(model_exp, 'rb') as f:
            graph_def = tf.GraphDef()
            graph_def.ParseFromString(f.read())
            tf.import_graph_def(graph_def, name='')
    else:
        print('Model directory: %s' % model_exp)
        meta_file, ckpt_file = get_model_filenames(model_exp)

        print('Metagraph file: %s' % meta_file)
        print('Checkpoint file: %s' % ckpt_file)

        saver = tf.train.import_meta_graph(os.path.join(model_exp, meta_file))
        saver.restore(tf.get_default_session(), os.path.join(model_exp, ckpt_file))

def get_model_filenames(model_dir):
    files = os.listdir(model_dir)
    meta_files = [s for s in files if s.endswith('.meta')]
    if len(meta_files) == 0:
        raise ValueError('No meta file found in the model directory (%s)' % model_dir)
    elif len(meta_files) > 1:
        raise ValueError('There should not be more than one meta file in the model directory (%s)' % model_dir)
    meta_file = meta_files[0]
    ckpt_file = None
    max_step = -1
    for f in files:
        step_str = re.match(r'(^model-[\w\- ]+.ckpt-(\d+))', f)
        if step_str is not None and len(step_str.groups()) >= 2:
            step = int(step_str.groups()[1])
            if step > max_step:
                max_step = step
                ckpt_file = step_str.groups()[0]
    return meta_file, ckpt_file

def face_align(image_size, img_arr, bounding_box):
    img_shape = np.asarray(img_arr.shape)[0:2]
    bb = np.zeros(4, dtype=np.int32)
    bb[0] = np.maximum(bounding_box[0] - margin / 2, 0)
    bb[1] = np.maximum(bounding_box[1] - margin / 2, 0)
    bb[2] = np.minimum(bounding_box[2] + margin / 2, img_shape[1])
    bb[3] = np.minimum(bounding_box[3] + margin / 2, img_shape[0])
    cropped = img_arr[bb[1]:bb[3], bb[0]:bb[2], :]
    aligned = misc.imresize(cropped, (image_size, image_size), interp='bilinear')
    prewhitened = prewhiten(aligned)

    return prewhitened

def prewhiten(x):
    mean = np.mean(x)
    std = np.std(x)
    std_adj = np.maximum(std, 1.0 / np.sqrt(x.size))
    y = np.multiply(np.subtract(x, mean), 1 / std_adj)
    return y


import serial
import time
import random
import math
import pygame

pygame.init()

DISPLAY_WIDTH  = 800
DISPLAY_HEIGHT = 600

gameDisplay = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
pygame.display.set_caption("Projeto Sist. Embarcados")

clock = pygame.time.Clock()

gray   = (39,40,34)
orange = (253,151,31)
pink   = (249,38,114)
blue   = (102,217,239)
green  = (166,226,46)
white  = (255,255,255)

ser = serial.Serial('/dev/ttyUSB0')
ser.flushInput()

def get_accel():
    ser_bytes = ser.readline().decode('utf-8')
    accel = ser_bytes.strip().split()

    accel_data = {  'ax' : int(accel[0].replace("\x00", ""))*0.006,
                    'ay' : int(accel[1].replace("\x00", ""))*0.006,
                    'az' : int(accel[2].replace("\x00", ""))*0.006}

    ax = accel_data['ax']
    ay = accel_data['ay']
    az = accel_data['az']

    xAngle = math.atan( ax / (math.sqrt(ay**2 + az**2)))
    yAngle = math.atan( ay / (math.sqrt(ax**2 + az**2)))
    zAngle = math.atan( math.sqrt(ax**2 + ay**2) / az)

    xAngle *= -180.00
    yAngle *= 180.00
    zAngle *= 180.00
    xAngle /= 3.141592
    yAngle /= 3.141592
    zAngle /= 3.141592

    accel_data = {  'ax' : xAngle,
                    'ay' : yAngle,
                    'az' : zAngle}

    print("x:{:10.2f}\ny:{:10.2f}\nz:{:10.2f}\n".format(accel_data['ax'],accel_data['ay'],accel_data['az']) )
    return accel_data

class Block(object):

    def __init__(self, x, y, width, height, color, speed):
        self.x = x
        self.y = y
        self.w = width
        self.h = height
        self.color = color
        self.speed = speed

    def draw(self):
        pygame.draw.rect(gameDisplay, self.color, [self.x, self.y, self.w, self.h])

    def update(self):
        accel_data = get_accel()

        self.y += accel_data['ay']/10
        self.x += accel_data['ax']/10

        if self.x > DISPLAY_WIDTH:
            self.x = DISPLAY_WIDTH

        if self.x < 0:
            self.x = 0

        if self.y > DISPLAY_HEIGHT:
            self.y = DISPLAY_HEIGHT

        if self.y < 0:
            self.y = 0

def game_loop():
    block = Block(DISPLAY_WIDTH//2, DISPLAY_HEIGHT//2, 10, 10, green, 1)

    while True:

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_q:
                    pygame.quit()
                    quit()

        gameDisplay.fill(gray)

        block.update()
        block.draw()

        pygame.display.update()
        clock.tick(60)

game_loop()

pygame.quit()
quit()