Ejemplo n.º 1
0
class Servo:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.sampling_rate = 10000
        self.PWM_frequency = 50
        self.mission = DAQmission()

    def power_on(self):
        self.mission.sendSingleDigital(channel='servo_power_1', value=True)


#        time.sleep(1.5)

    def power_off(self):
        self.mission.sendSingleDigital(channel='servo_power_1', value=False)

    def rotate(self, degree):
        # Convert degree to duty cycle in PWM.
        if degree >= 0 and degree <= 360:
            dutycycle = 0.02  #round((degree/180) * 0.05 + 0.05, 6)

            PWM_wave = self.blockWave(self.sampling_rate,
                                      self.PWM_frequency,
                                      dutycycle,
                                      repeats=50)

            PWM_wave = np.where(PWM_wave == 0, False, True)
            #            plt.figure()
            #            plt.plot(PWM_wave)
            #            plt.show()
            PWM_wave_organized = np.array(
                [('servo_modulation_1', PWM_wave),
                 ('servo_power_1', np.ones(len(PWM_wave), dtype=bool))],
                dtype=[('Sepcification', 'U20'),
                       ('Waveform', bool, (len(PWM_wave), ))])

            self.mission.runWaveforms(clock_source = "DAQ", sampling_rate = self.sampling_rate, analog_signals = {},\
                                    digital_signals = PWM_wave_organized, readin_channels = {})

        else:
            print('Rotation degree out of range!')

    def blockWave(self,
                  sampleRate,
                  frequency,
                  dutycycle,
                  repeats,
                  voltMin=0,
                  voltMax=5):
        """
        Generates a one period blockwave. 
        sampleRate      samplerate set on the DAQ (int)
        frequency       frequency you want for the block wave (int)
        voltMin         minimum value of the blockwave (float)
        voltMax         maximum value of the blockwave (float)
        dutycycle       duty cycle of the wave (wavelength at voltMax) (float)
        """
        wavelength = int(sampleRate /
                         frequency)  #Wavelength in number of samples
        #The high values
        high = np.ones(math.ceil(wavelength * dutycycle)) * voltMax
        #Low values
        low = np.ones(math.floor(wavelength * (1 - dutycycle))) * voltMin
        #Adding them
        single_period = np.append(high, low)
        """
        Repeats the wave a set number of times and returns a new repeated wave.
        """
        extendedWave = np.array([])
        for i in range(repeats):
            extendedWave = np.append(extendedWave, single_period)

        return extendedWave
class ScanningExecutionThread(QThread):
    
    ScanningResult = pyqtSignal(np.ndarray, np.ndarray, object, object) #The signal for the measurement, we can connect to this signal
    #%%
    def __init__(self, RoundQueueDict, RoundCoordsDict, GeneralSettingDict, *args, **kwargs):        
        super().__init__(*args, **kwargs)
        self.RoundQueueDict = RoundQueueDict
        self.RoundCoordsDict = RoundCoordsDict
        self.GeneralSettingDict = GeneralSettingDict
        self.Status_list = None
        self.ludlStage = LudlStage("COM12")
        self.watchdog_flag = True
        
        self.clock_source = 'DAQ' # Should be set by GUI.
        
        self.scansavedirectory = self.GeneralSettingDict['savedirectory']
        self.meshgridnumber = int(self.GeneralSettingDict['Meshgrid'])
        
        self.wavelength_offset = 0 # An offset of 0.002 mm is observed between 900 and 1280 nm.
    #%%
    def run(self):
        """~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        #                                                               Connect devices.
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""
        """
        # =============================================================================
        #         connect the Objective motor
        # =============================================================================
        """
        print('----------------------Starting to connect the Objective motor-------------------------')
        self.pi_device_instance = PIMotor()
        print('Objective motor connected.')
        self.errornum = 0
        self.init_focus_position = self.pi_device_instance.pidevice.qPOS(self.pi_device_instance.pidevice.axes)['1']
        print("init_focus_position : {}".format(self.init_focus_position))

        """
        # =============================================================================
        #         connect the Hmamatsu camera
        # =============================================================================
        """
        self._use_camera = False
        # Check if camera is used.
        for key in self.RoundQueueDict:
            if "RoundPackage_" in key:
                for waveform_and_cam_key in self.RoundQueueDict[key][1]:
                    if "CameraPackage_" in waveform_and_cam_key:
                        if len(self.RoundQueueDict[key][1][waveform_and_cam_key]) != 0:
                            self._use_camera = True
        
        if self._use_camera:
            print('Connecting camera...')
            self.HamamatsuCam = CamActuator()
            self.HamamatsuCam.initializeCamera()
        else:
            print('No camera involved.')
        
        """
        # =============================================================================
        #         connect the Insight X3
        # =============================================================================
        """        
        if len(self.RoundQueueDict['InsightEvents']) != 0:
            self.Laserinstance = InsightX3('COM11')
            try:
                # querygap = 1.1
                # self.Laserinstance.SetWatchdogTimer(0) # Disable the laser watchdog!
#                Status_watchdog_thread = threading.Thread(target = self.Status_watchdog, args=[querygap], daemon=True)
#                Status_watchdog_thread.start() 
                # time.sleep(1)
                #-------------Initialize laser--------------
                self.watchdog_flag = False
                time.sleep(0.5)
        
                warmupstatus = 0
                while int(warmupstatus) != 100:
                    warmupstatus = self.Laserinstance.QueryWarmupTime()
                    time.sleep(0.6)
                        
                self.watchdog_flag = True
                time.sleep(0.5)
            except:
                print('Laser not connected.')
                
            # If turn on the laser shutter in the beginning
            if 'Shutter_Open' in self.GeneralSettingDict['StartUpEvents']:
                time.sleep(0.5)

                self.Laserinstance.Open_TunableBeamShutter()

                time.sleep(0.5)     
        """
        # =============================================================================
        #         Initialize ML
        # =============================================================================
        """        
        self._use_ML = False
        # Check if machine learning segmentation is used.
        for key in self.RoundQueueDict:
            if "RoundPackage_" in key:
                for photocycle_key in self.RoundQueueDict[key][2]:
                    if "PhotocyclePackage_" in photocycle_key:
                        if len(self.RoundQueueDict[key][2][photocycle_key]) != 0:
                            self._use_ML = True
        if self._use_ML:
            from ImageAnalysis.ImageProcessing_MaskRCNN import ProcessImageML
            self.Predictor = ProcessImageML()
            print("ML loaded.")
            
        #%%
        """~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        #                                                                  Execution
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""
        GridSequence = 0
        TotalGridNumber = self.meshgridnumber**2
        
        for EachGrid in range(TotalGridNumber):
            """
            #------------------------------------------------------------------------------
            #:::::::::::::::::::::::::::::::: AT EACH GRID ::::::::::::::::::::::::::::::::
            #------------------------------------------------------------------------------
            """
            self.Grid_index = EachGrid
            """
            # =============================================================================
            #         For each small repeat unit in the scanning meshgrid
            # =============================================================================
            """

            time.sleep(0.5)
            
            for EachRound in range(int(len(self.RoundQueueDict)/2-1)): # EachRound is the round sequence number starting from 0, while the actual number used in dictionary is 1.
                """
                #-------------------------------------------------------------------------------
                #:::::::::::::::::::::::::::::::: AT EACH ROUND ::::::::::::::::::::::::::::::::
                #-------------------------------------------------------------------------------
                """
                print ('----------------------------------------------------------------------------')            
                print('Below is Grid {}, Round {}.'.format(EachGrid, EachRound+1)) # EachRound+1 is the corresponding round number when setting the dictionary starting from round 1.
                """
                # =============================================================================
                #         Execute Insight event at the beginning of each round
                # =============================================================================
                """            
                self.laser_init(EachRound)
                    
                """
                # =============================================================================
                #         Execute filter event at the beginning of each round
                # =============================================================================
                """
                self.filters_init(EachRound)

                """
                # =============================================================================
                #         Generate focus position list at the beginning of each round
                # =============================================================================
                """
                if EachRound == 0:
                    ZStackinfor = self.GeneralSettingDict['FocusStackInfoDict']['RoundPackage_{}'.format(EachRound+1)]        
                    ZStackNum = int(ZStackinfor[ZStackinfor.index('Focus')+5])
                    ZStackStep = float(ZStackinfor[ZStackinfor.index('Being')+5:len(ZStackinfor)])
            
                    # Generate position list.
                    ZStacklinspaceStart = self.init_focus_position - (math.floor(ZStackNum/2)) * ZStackStep
                    ZStacklinspaceEnd = self.init_focus_position + (ZStackNum - math.floor(ZStackNum/2)-1) * ZStackStep
    
                    self.ZStackPosList = np.linspace(ZStacklinspaceStart, ZStacklinspaceEnd, num = ZStackNum)
                
                self.currentCoordsSeq = 0
                #%%
                #-------------Unpack infor for stage move.
                CoordsNum = len(self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)])              
                for EachCoord in range(CoordsNum):
                    """
                    #------------------------------------------------------------------------------------
                    #:::::::::::::::::::::::::::::::: AT EACH COORDINATE ::::::::::::::::::::::::::::::::
                    #------------------------------------------------------------------------------------
                    """
                    self.error_massage = None
                    
                    self.currentCoordsSeq += 1
                    
                    """
                    # =============================================================================
                    #         Stage movement
                    # =============================================================================
                    """
                    self.coord_array = self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)][EachCoord]
                    
                    # Offset coordinate row value for each well.
                    ScanningGridOffset_Row = int(EachGrid % self.meshgridnumber) * (self.GeneralSettingDict['StageGridOffset']) 
                    # Offset coordinate colunm value for each well.
                    ScanningGridOffset_Col = int(EachGrid/self.meshgridnumber) * (self.GeneralSettingDict['StageGridOffset']) 

                    RowIndex = self.coord_array['row'] + ScanningGridOffset_Row
                    ColumnIndex = self.coord_array['col'] + ScanningGridOffset_Col
                                      
                    try:
                        # for _ in range(2): # Repeat twice
                        #     self.ludlStage.moveAbs(RowIndex,ColumnIndex) # Row/Column indexs of np.array are opposite of stage row-col indexs.
                        #     time.sleep(1)
                        self.ludlStage.moveAbs(RowIndex,ColumnIndex) # Row/Column indexs of np.array are opposite of stage row-col indexs.
                        time.sleep(1.2)
                    except:
                        self.error_massage = 'Fail_MoveStage'
                        self.errornum += 1
                        print('Stage move failed! Error number: {}'.format(int(self.errornum)))

                    time.sleep(0.2)
                    
                    """
                    # =============================================================================
                    #                               Focus position
                    #         Unpack the focus stack information, conduct auto-focusing if set.
                    # =============================================================================
                    """
                    # Here also generate the ZStackPosList.
                    self.ZStackNum = self.unpack_focus_stack(EachGrid, EachRound, EachCoord)
                    
                    self.stack_focus_degree_list = []
                    
                    print('*******************************************Round {}. Current index: {}.**************************************************'.format\
                          (EachRound+1, [RowIndex,ColumnIndex]))
                    
                    #------------------Move to Z stack focus-------------------
                    for EachZStackPos in range(self.ZStackNum): 
                        """
                        #------------------------------------------------------------------------------------
                        #:::::::::::::::::::::::::::::::: AT EACH ZSTACK ::::::::::::::::::::::::::::::::::::
                        #------------------------------------------------------------------------------------
                        """
                        print('--------------------------------------------Stack {}--------------------------------------------------'.format(EachZStackPos+1))
                        if self.ZStackNum > 1:
                            self.ZStackOrder = int(EachZStackPos +1) # Here the first one is 1, not starting from 0.
                            
                            self.FocusPos = self.ZStackPosList[EachZStackPos]# + self.wavelength_offset    
                            
                            # Add the focus degree of previous image to the list.
                            # For stack of 3 only.
                            if EachZStackPos > 0:
                                try:
                                    self.stack_focus_degree_list.append(self.FocusDegree_img_reconstructed)
                                    
                                except:
                                    # FocusDegree_img_reconstructed is not generated with camera imaging.
                                    pass
                            print('stack_focus_degree_list is {}'.format(self.stack_focus_degree_list))
                            #-----Suppose now it's the 3rd stack position-----
                            if len(self.stack_focus_degree_list) == 2:
                                if self.stack_focus_degree_list[-1] < self.stack_focus_degree_list[-2]:
                                    self.FocusPos = self.ZStackPosList[0] - (self.ZStackPosList[1] - self.ZStackPosList[0])/2
                                    print('Focus degree decreasing, run the other direction.')
                            #-------------------------------------------------
                            
                            print('Target focus pos: {}'.format(self.FocusPos))
    
                            self.pi_device_instance.move(self.FocusPos)
                            # self.auto_focus_positionInStack = self.pi_device_instance.pidevice.qPOS(self.pi_device_instance.pidevice.axes)
                            # print("Current position: {:.4f}".format(self.auto_focus_positionInStack['1']))
                            
                            time.sleep(0.3)
                        else:
                            self.ZStackOrder = 1
                            self.FocusPos = self.init_focus_position
                        """
                        # =============================================================================
                        #         Execute waveform packages
                        # =============================================================================
                        """
                        self.Waveform_sequence_Num = int(len(self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][0]))                        
                        #------------For waveforms in each coordinate----------
                        for EachWaveform in range(self.Waveform_sequence_Num):
                            
                            """
                            # =============================================================================
                            #         For photo-cycle 
                            # =============================================================================
                            """
                            # Get the photo cycle information
                            PhotocyclePackageToBeExecute = self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][2] \
                                                            ["PhotocyclePackage_{}".format(EachWaveform+1)]
                            
                            # See if in this waveform sequence photo cycle is involved.
                            # PhotocyclePackageToBeExecute is {} if not configured.
                            if len(PhotocyclePackageToBeExecute) > 0:
                                
                                
                                
                                # Load the previous acquired camera image
                                self.cam_tif_name = r"M:\tnw\ist\do\projects\Neurophotonics\Brinkslab\Data\Octoscope\2020-8-13 Screening Archon1 library V5 and 6\V6\Round2_Coords181_R19800C0_PMT_0Zmax.tif"
                                previous_cam_img = imread(self.cam_tif_name)
                                img_width = previous_cam_img.shape[1]

                                img_height = previous_cam_img.shape[0]

                                # Get the segmentation of full image.
                                fig, ax = plt.subplots()
                                MLresults = self.Predictor.DetectionOnImage(previous_cam_img, axis = ax)
                                
                                ROI_number = len(MLresults["scores"])
                                print('roi number: {}'.format(ROI_number))
                                for each_ROI in range(ROI_number):
                                    # if MLresults['class_ids'][each_ROI] == 3:
                                    ROIlist = MLresults['rois'][each_ROI]
                                    print(ROIlist)
                                    # np array's column([1]) is the width of image, and is the row in stage coordinates.
                                    ROI_center_width = int(ROIlist[1] + (ROIlist[3] - ROIlist[1])/2)
                                    print('ROI_center_width '.format(ROIlist))
                                    ROI_center_height = int(ROIlist[0] + (ROIlist[2] + ROIlist[0])/2)
                                    print('ROI_center_height '.format(ROIlist))
                                    cam_stage_transform_factor = 1.135
                                    
                                    stage_move_col = (int((img_width)/2) - ROI_center_width) * cam_stage_transform_factor
                                    print(stage_move_col)
                                    stage_move_row = (int((img_height)/2) - ROI_center_height) * cam_stage_transform_factor
                                    print(stage_move_row)
                                    # Move cell of interest to the center of field of view
                                    self.ludlStage.moveRel(xRel = stage_move_row, yRel= stage_move_col)
                                    
                                    time.sleep(1)
                                    
                                    # Move the cell back
                                    self.ludlStage.moveRel(xRel = -1*stage_move_row, yRel= -1*stage_move_col)
                                    time.sleep(1)
                            #--------------------------------------------------
                            
                            """
                            # =============================================================================
                            #         Execute pre-set operations at EACH COORDINATE.
                            # =============================================================================
                            """
                            self.inidividual_coordinate_operation(EachRound, EachWaveform, RowIndex, ColumnIndex)
                                
                            
                        time.sleep(0.6) # Wait for receiving data to be done.
                    time.sleep(0.5)
                    print('*************************************************************************************************************************')
                    
        #%%
        """~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        #                                                            Disconnect devices.
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""             
        
        # Switch off laser
        if len(self.RoundQueueDict['InsightEvents']) != 0:
            self.watchdog_flag = False
            time.sleep(0.5)

            self.Laserinstance.Close_TunableBeamShutter()

            time.sleep(0.5)
            
            self.Laserinstance.SaveVariables()
                     
            self.Laserinstance.Turn_Off_PumpLaser()
                    
        # Disconnect camera
        if self._use_camera == True:
            self.HamamatsuCam.Exit()
        
        # Disconnect focus motor
        try:
            self.pi_device_instance.CloseMotorConnection()
            print('Objective motor disconnected.')
        except:
            pass
        
    #%%
    def laser_init(self, EachRound):
        """
        Execute Insight event at the beginning of each round

        Parameters
        ----------
        EachRound : int
            Round index.

        Returns
        -------
        None.

        """
        #-Unpack infor for Insight X3. In the list, the first one is for shutter event and the second one is for wavelength event. 
        InsightX3EventIndexList = [i for i,x in enumerate(self.RoundQueueDict['InsightEvents']) if 'Round_{}'.format(EachRound+1) in x]
          
        if len(InsightX3EventIndexList) == 1:
            print(InsightX3EventIndexList)
            InsightText = self.RoundQueueDict['InsightEvents'][InsightX3EventIndexList[0]]
            if 'Shutter_Open' in InsightText:
                self.watchdog_flag = False
                time.sleep(0.5)

                self.Laserinstance.Open_TunableBeamShutter()

                time.sleep(0.5)
                print('Laser shutter open.')
                self.watchdog_flag = True
                time.sleep(0.5)

            elif 'Shutter_Close' in InsightText:
                self.watchdog_flag = False
                time.sleep(0.5)

                self.Laserinstance.Close_TunableBeamShutter()

                time.sleep(0.5)
                print('Laser shutter closed.')
                self.watchdog_flag = True
                time.sleep(0.5)
            elif 'WavelengthTo' in InsightText:
                self.watchdog_flag = False
                time.sleep(0.5)
                TargetWavelen = int(InsightText[InsightText.index('To_')+3:len(InsightText)])
                
                if TargetWavelen == 1280:
                    self.wavelength_offset = -0.002 # give an offset if wavelength goes to 1280.
                elif TargetWavelen == 900:
                    self.wavelength_offset = 0
                    
                self.Laserinstance.SetWavelength(TargetWavelen)

                time.sleep(5)
                self.watchdog_flag = True
                time.sleep(0.5)
                
        elif len(InsightX3EventIndexList) == 2:
            
            InsightText_wl = self.RoundQueueDict['InsightEvents'][InsightX3EventIndexList[1]]
            InsightText_st = self.RoundQueueDict['InsightEvents'][InsightX3EventIndexList[0]]
            
            if 'WavelengthTo' in InsightText_wl and 'Shutter_Open' in InsightText_st:
                self.watchdog_flag = False
                time.sleep(0.5)
                TargetWavelen = int(InsightText_wl[InsightText_wl.index('To_')+3:len(InsightText_wl)])

                self.Laserinstance.SetWavelength(TargetWavelen)

                if TargetWavelen == 1280:
                    self.wavelength_offset = -0.002 # give an offset if wavelength goes to 1280.
                elif TargetWavelen == 900:
                    self.wavelength_offset = 0
                    
                time.sleep(5)

                self.Laserinstance.Open_TunableBeamShutter()

                print('Laser shutter open.')
                self.watchdog_flag = True
                time.sleep(0.5)
                
            elif 'WavelengthTo' in InsightText_wl and 'Shutter_Close' in InsightText_st:
                self.watchdog_flag = False
                time.sleep(0.5)
                TargetWavelen = int(InsightText_wl[InsightText_wl.index('To_')+3:len(InsightText_wl)])
               
                self.Laserinstance.SetWavelength(TargetWavelen)

                if TargetWavelen == 1280:
                    self.wavelength_offset = -0.002 # give an offset if wavelength goes to 1280.
                elif TargetWavelen == 900:
                    self.wavelength_offset = 0
                    
                time.sleep(5)

                self.Laserinstance.Close_TunableBeamShutter()

                time.sleep(1)
                print('Laser shutter closed.')
                self.watchdog_flag = True
                time.sleep(0.5)
                
            time.sleep(2)
            
    def filters_init(self, EachRound):
        """
        Execute filter event at the beginning of each round.

        Parameters
        ----------
        EachRound : int
            Round index.

        Returns
        -------
        None.

        """
        
        #-Unpack infor for filter event. In the list, the first one is for ND filter and the second one is for emission filter.
        FilterEventIndexList = [i for i,x in enumerate(self.RoundQueueDict['FilterEvents']) if 'Round_{}'.format(EachRound+1) in x]
        
        if len(FilterEventIndexList) > 0:
            NDposText = self.RoundQueueDict['FilterEvents'][FilterEventIndexList[0]]
            NDnumber = NDposText[NDposText.index('ToPos_')+6:len(NDposText)]
            
            EMposText = self.RoundQueueDict['FilterEvents'][FilterEventIndexList[1]]
            EMprotein = EMposText[EMposText.index('ToPos_')+6:len(EMposText)]
            
            # "COM9" for filter 1 port, which has ND values from 0 to 3.
            # "COM7" for filter 2 port, which has ND values from 0 to 0.5.
            if NDnumber == '0':
                ND_filter1_Pos = 0
                ND_filter2_Pos = 0
            elif NDnumber == '1':
                ND_filter1_Pos = 1
                ND_filter2_Pos = 0
            elif NDnumber == '2':
                ND_filter1_Pos = 2
                ND_filter2_Pos = 0
            elif NDnumber == '2.3':
                ND_filter1_Pos = 2
                ND_filter2_Pos = 2
            elif NDnumber == '2.5':
                ND_filter1_Pos = 2
                ND_filter2_Pos = 3
            elif NDnumber == '0.5':
                ND_filter1_Pos = 0
                ND_filter2_Pos = 3        
            elif NDnumber == '0.3':
                ND_filter1_Pos = 0
                ND_filter2_Pos = 2
            
            if EMprotein == 'Arch':
                EM_filter_Pos = 0
            elif EMprotein == 'eGFP' or EMprotein == 'Citrine':
                EM_filter_Pos = 1
            
            # Execution
            if ND_filter1_Pos != None and ND_filter2_Pos != None:
                #Move filter 1
                self.filter1 = ELL9Filter("COM9")
                self.filter1.moveToPosition(ND_filter1_Pos)
                time.sleep(1)
                #Move filter 2
                self.filter2 = ELL9Filter("COM7")
                self.filter2.moveToPosition(ND_filter2_Pos)
                time.sleep(1)
            if EM_filter_Pos != None:
                self.filter3 = ELL9Filter("COM15")
                self.filter3.moveToPosition(EM_filter_Pos)
                time.sleep(1)
    
    def unpack_focus_stack(self, EachGrid, EachRound, EachCoord):
        """
        Unpack the focus stack information.
        Determine focus position either from pre-set numbers of by auto-focusing.
        
        Parameters
        ----------
        EachGrid : int
            Current grid index.
        EachRound : int
            Current round index.
        EachCoord : int
            Current coordinate index.

        Returns
        -------
        ZStackNum : int
            Number of focus positions in stack.
        ZStackPosList : list
            List of focus stack positions for objective to go to.

        """
        ZStackinfor = self.GeneralSettingDict['FocusStackInfoDict']['RoundPackage_{}'.format(EachRound+1)]        
        ZStackNum = int(ZStackinfor[ZStackinfor.index('Focus')+5])
        ZStackStep = float(ZStackinfor[ZStackinfor.index('Being')+5:len(ZStackinfor)])
        
        # If manual focus correction applies, unpact the target focus infor.
        if len(self.GeneralSettingDict['FocusCorrectionMatrixDict']) > 0:
            FocusPosArray = self.GeneralSettingDict['FocusCorrectionMatrixDict']['RoundPackage_{}_Grid_{}'.format(EachRound+1, EachGrid)]
            FocusPosArray = FocusPosArray.flatten('F')
            FocusPos_fromCorrection = FocusPosArray[EachCoord]
            
            ZStacklinspaceStart = FocusPos_fromCorrection - (math.floor(ZStackNum/2))* ZStackStep
            ZStacklinspaceEnd = FocusPos_fromCorrection + (ZStackNum - math.floor(ZStackNum/2)-1) * ZStackStep         
        
        # With auto-focus correction
        else:
            # If go for auto-focus at this coordinate
            auto_focus_flag = self.coord_array['auto_focus_flag']
            # auto_focus_flag = False
            print('focus_position {}'.format(self.coord_array['focus_position']))
            
            #-----------------------Auto focus---------------------------------
            if auto_focus_flag == "yes":
                
                if self.coord_array['focus_position'] == -1.:
                    instance_FocusFinder = FocusFinder(motor_handle = self.pi_device_instance)
                    print("--------------Start auto-focusing-----------------")
                    # instance_FocusFinder.total_step_number = 7
                    # instance_FocusFinder.init_step_size = 0.013
                    # self.auto_focus_position = instance_FocusFinder.gaussian_fit()
                    self.auto_focus_position = instance_FocusFinder.bisection()
                    
                    relative_move_coords = [[1550,0],[0,1550],[1550,1550]]
                    trial_num = 0
                    while self.auto_focus_position == False: # If there's no cell in FOV
                        if trial_num <= 2:
                            print('No cells found. move to next pos.')
                            # Move to next position in real scanning coordinates.
                            self.ludlStage.moveRel(relative_move_coords[trial_num][0],relative_move_coords[trial_num][1])
                            time.sleep(1)
                            print('Now stage pos is {}'.format(self.ludlStage.getPos()))
                            
                            self.auto_focus_position = instance_FocusFinder.bisection()
                            # Move back 
                            self.ludlStage.moveRel(-1*relative_move_coords[trial_num][0],-1*relative_move_coords[trial_num][1])
                            
                            trial_num += 1
                        else:
                            print('No cells in neighbouring area.')
                            self.auto_focus_position = self.ZStackPosList[int(len(self.ZStackPosList)/2)]
                            break
                        
                    print("--------------End of auto-focusing----------------")
                    time.sleep(1)
      
                    # Record the position, try to write it in the NEXT round dict.
                    try:
                        self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound + 2)][EachCoord]['focus_position'] = self.auto_focus_position
                    except:# If it's already the last round, skip.
                        pass
                    
                    # Generate position list.
                    ZStacklinspaceStart = self.auto_focus_position - (math.floor(ZStackNum/2)) * ZStackStep
                    ZStacklinspaceEnd = self.auto_focus_position + (ZStackNum - math.floor(ZStackNum/2)-1) * ZStackStep
                
                else: # If there's already position from last round, move to it.
                    self.previous_auto_focus_position = self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)][EachCoord]['focus_position']
                    
                    try:
                        # Record the position, try to write it in the NEXT round dict. 
                        self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound + 2)][EachCoord]['focus_position'] = self.previous_auto_focus_position
                    except:
                        pass
                    
                    # Generate position list.
                    ZStacklinspaceStart = self.previous_auto_focus_position - (math.floor(ZStackNum/2)) * ZStackStep
                    ZStacklinspaceEnd = self.previous_auto_focus_position + (ZStackNum - math.floor(ZStackNum/2)-1) * ZStackStep
                    
                    # self.previous_auto_focus_position = self.auto_focus_position
                    # print("=====================Move to last recorded position=================")
                    # self.pi_device_instance.move(self.previous_auto_focus_position)
                    # time.sleep(0.2)
                    
                    # instance_FocusFinder = FocusFinder(motor_handle = self.pi_device_instance)
                    # print("--------------Start auto-focusing-----------------")
                    # self.auto_focus_position = instance_FocusFinder.bisection()
                    
                    # try:
                    #     if self.auto_focus_position[0] == False: # If there's no cell in FOV
                    #     # Skip?  mid_position = [False, self.current_pos]
                    #         self.auto_focus_position = self.auto_focus_position[1]
                    # except:
                    #     pass
                        
                    # print("--------------End of auto-focusing----------------")
                    # time.sleep(1)
      
                    # # Record the position, try to write it in the NEXT round dict.
                    # try:
                    #     self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound + 2)][EachCoord]['focus_position'] = self.auto_focus_position
                    # except:# If it's already the last round, skip.
                    #     pass                    
                

                # Generate the position list, for none-auto-focus coordinates they will use the same list variable.
                self.ZStackPosList = np.linspace(ZStacklinspaceStart, ZStacklinspaceEnd, num = ZStackNum)
                print('ZStackPos is : {}'.format(self.ZStackPosList))   
            #------------------------------------------------------------------
            # If not auto focus, use the same list variable self.ZStackPosList.
            else:
                pass
                
        return ZStackNum
        
    def inidividual_coordinate_operation(self, EachRound, EachWaveform, RowIndex, ColumnIndex):
        """
        Execute pre-set operations at each coordinate.

        Parameters
        ----------
        EachRound : int
            Current round index.
        EachWaveform : int
            Current waveform package index.
        RowIndex : int
            Current sample stage row index.
        ColumnIndex : int
            Current sample stage row index.

        Returns
        -------
        None.

        """
        # Extract information
        WaveformPackageToBeExecute = self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][0]['WaveformPackage_{}'.format(EachWaveform+1)]
        CameraPackageToBeExecute = self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][1]["CameraPackage_{}".format(EachWaveform+1)]                  
        WaveformPackageGalvoInfor = self.RoundQueueDict['GalvoInforPackage_{}'.format(EachRound+1)]['GalvoInfor_{}'.format(EachWaveform+1)]

        self.readinchan = WaveformPackageToBeExecute[3]
        self.RoundWaveformIndex = [EachRound+1, EachWaveform+1] # first is current round number, second is current waveform package number.
        self.CurrentPosIndex = [RowIndex, ColumnIndex]
        
        #----------------Camera operations-----------------
        _camera_isUsed = False
        if CameraPackageToBeExecute != {}: # if camera operations are configured
            _camera_isUsed = True
            CamSettigList = CameraPackageToBeExecute["Settings"]
            self.HamamatsuCam.StartStreaming(BufferNumber = CameraPackageToBeExecute["Buffer_number"],
                                             trigger_source = CamSettigList[CamSettigList.index("trigger_source")+1],
                                             exposure_time = CamSettigList[CamSettigList.index("exposure_time")+1],
                                             trigger_active = CamSettigList[CamSettigList.index("trigger_active")+1],
                                             subarray_hsize = CamSettigList[CamSettigList.index("subarray_hsize")+1],
                                             subarray_vsize = CamSettigList[CamSettigList.index("subarray_vsize")+1],
                                             subarray_hpos = CamSettigList[CamSettigList.index("subarray_hpos")+1],
                                             subarray_vpos = CamSettigList[CamSettigList.index("subarray_vpos")+1])
            # HamamatsuCam starts another thread to pull out frames from buffer.
            # Make sure that the camera is prepared before waveform execution.
#                                while self.HamamatsuCam.isStreaming == False:
#                                    print('Waiting for camera...')
#                                    time.sleep(0.5)
            time.sleep(1)
        print('Now start waveforms')
        #----------------Waveforms operations--------------
        if WaveformPackageGalvoInfor != 'NoGalvo': # Unpack the information of galvo scanning.
            self.readinchan = WaveformPackageGalvoInfor[0]
            self.repeatnum = WaveformPackageGalvoInfor[1]
            self.PMT_data_index_array = WaveformPackageGalvoInfor[2]
            self.averagenum = WaveformPackageGalvoInfor[3]
            self.lenSample_1 = WaveformPackageGalvoInfor[4]
            self.ScanArrayXnum = WaveformPackageGalvoInfor[5]
        
        self.adcollector = DAQmission()
        # self.adcollector.collected_data.connect(self.ProcessData)
        self.adcollector.runWaveforms(clock_source = self.clock_source, sampling_rate = WaveformPackageToBeExecute[0],
                                      analog_signals = WaveformPackageToBeExecute[1], digital_signals = WaveformPackageToBeExecute[2], 
                                      readin_channels = WaveformPackageToBeExecute[3])
        self.adcollector.save_as_binary(self.scansavedirectory)
        self.recorded_raw_data = self.adcollector.get_raw_data()
        self.Process_raw_data()
        
        #------------------Camera saving-------------------
        if _camera_isUsed == True:
            self.HamamatsuCam.isSaving = True
            img_text =  "_Cam_"+str(self.RoundWaveformIndex[1])+"_Zpos" + str(self.ZStackOrder)
            self.cam_tif_name = self.generate_tif_name(extra_text = img_text)
            self.HamamatsuCam.StopStreaming(saving_dir = self.cam_tif_name)
            # Make sure that the saving process is finished.
            while self.HamamatsuCam.isSaving == True:
                print('Camera saving...')
                time.sleep(0.5)
            time.sleep(1) 

        time.sleep(0.5)      
        
    def Process_raw_data(self):

        self.channel_number = len(self.recorded_raw_data)
        
        self.data_collected_0 = self.recorded_raw_data[0][0:len(self.recorded_raw_data[0])-1]*-1
        print(len(self.data_collected_0))         
        
        if self.channel_number == 1:            
            if 'Vp' in self.readinchan:
                pass
            elif 'Ip' in self.readinchan:
                pass
            elif 'PMT' in self.readinchan:  # repeatnum, PMT_data_index_array, averagenum, ScanArrayXnum
                # Reconstruct the image from np array and save it.
                self.PMT_image_processing()
                
        elif self.channel_number == 2: 
            if 'PMT' not in self.readinchan:
                pass
            elif 'PMT' in self.readinchan:

                self.PMT_image_processing()

        print('ProcessData executed.')        
        

    # def ProcessData(self, data_waveformreceived):
    #     print('ZStackOrder is:'+str(self.ZStackOrder)+'numis_'+str(self.ZStackNum))
    #     self.adcollector.save_as_binary(self.scansavedirectory)
        
    #     self.channel_number = len(data_waveformreceived)
        
    #     self.data_collected_0 = data_waveformreceived[0]*-1
    #     self.data_collected_0 = self.data_collected_0[0:len(self.data_collected_0)-1]
    #     print(len(self.data_collected_0))
        
    #     if self.channel_number == 1:            
    #         if 'Vp' in self.readinchan:
    #             pass
    #         elif 'Ip' in self.readinchan:
    #             pass
    #         elif 'PMT' in self.readinchan:  # repeatnum, PMT_data_index_array, averagenum, ScanArrayXnum

    #             # Reconstruct the image from np array and save it.
    #             self.PMT_image_processing()
                
    #     elif self.channel_number == 2: 
    #         if 'PMT' not in self.readinchan:
    #             pass
    #         elif 'PMT' in self.readinchan:
                
    #             self.PMT_image_processing()

    #     print('ProcessData executed.')

    def PMT_image_processing(self):
        """
        Reconstruct the image from np array and save it.

        Returns
        -------
        None.

        """
        for imageSequence in range(self.repeatnum):
            
            try:
                self.PMT_image_reconstructed_array = self.data_collected_0[np.where(self.PMT_data_index_array == imageSequence+1)]

                Dataholder_average = np.mean(self.PMT_image_reconstructed_array.reshape(self.averagenum, -1), axis=0)

                Value_yPixels = int(self.lenSample_1/self.ScanArrayXnum)
                self.PMT_image_reconstructed = np.reshape(Dataholder_average, (Value_yPixels, self.ScanArrayXnum))
                
                self.PMT_image_reconstructed = self.PMT_image_reconstructed[:, 50:550] # Crop size based on: M:\tnw\ist\do\projects\Neurophotonics\Brinkslab\Data\Xin\2019-12-30 2p beads area test 4um
                # self.PMT_image_reconstructed = self.PMT_image_reconstructed[:, 70:326] # for 256*256 images
                
                # Evaluate the focus degree of re-constructed image.
                self.FocusDegree_img_reconstructed = ProcessImage.local_entropy(self.PMT_image_reconstructed.astype('float32'))
                print('FocusDegree_img_reconstructed is {}'.format(self.FocusDegree_img_reconstructed))
                
                # Save the individual file.
                with skimtiff.TiffWriter(os.path.join(self.scansavedirectory, 'Round'+str(self.RoundWaveformIndex[0]) + '_Grid' + str(self.Grid_index) +'_Coords'+str(self.currentCoordsSeq)+'_R'+str(self.CurrentPosIndex[0])+'C'+str(self.CurrentPosIndex[1])+'_PMT_'+str(imageSequence)+'Zpos'+str(self.ZStackOrder)+'.tif'), imagej = True) as tif:                
                    tif.save(self.PMT_image_reconstructed.astype('float32'), compress=0, metadata = {"FocusPos: " : str(self.FocusPos)})
               
                plt.figure()
                plt.imshow(self.PMT_image_reconstructed, cmap = plt.cm.gray) # For reconstructed image we pull out the first layer, getting 2d img.
                plt.show()                

                #---------------------------------------------For multiple images in one z pos, Stack the arrays into a 3d array--------------------------------------------------------------------------
                # if imageSequence == 0:
                #     self.PMT_image_reconstructed_stack = self.PMT_image_reconstructed[np.newaxis, :, :] # Turns into 3d array
                # else:
                #     self.PMT_image_reconstructed_stack = np.concatenate((self.PMT_image_reconstructed_stack, self.PMT_image_reconstructed[np.newaxis, :, :]), axis=0)
                #     print(self.PMT_image_reconstructed_stack.shape)
                    
                #---------------------------------------------Calculate the z max projection-----------------------------------------------------------------------
                if self.repeatnum == 1: # Consider one repeat image situlation 
                    if self.ZStackNum > 1:
                        if self.ZStackOrder == 1:
                            self.PMT_image_maxprojection_stack = self.PMT_image_reconstructed[np.newaxis, :, :]

                        else:

                            self.PMT_image_maxprojection_stack = np.concatenate((self.PMT_image_maxprojection_stack, self.PMT_image_reconstructed[np.newaxis, :, :]), axis=0)

                    else:
                        self.PMT_image_maxprojection_stack = self.PMT_image_reconstructed[np.newaxis, :, :]
                        
                # Save the max projection image
                if self.ZStackOrder == self.ZStackNum:
                    self.PMT_image_maxprojection = np.max(self.PMT_image_maxprojection_stack, axis=0)
                    
                    # Save the zmax file.
                    with skimtiff.TiffWriter(os.path.join(self.scansavedirectory, 'Round'+str(self.RoundWaveformIndex[0])+ '_Grid' + str(self.Grid_index) + '_Coords'+str(self.currentCoordsSeq)+'_R'+str(self.CurrentPosIndex[0])+'C'+str(self.CurrentPosIndex[1])+'_PMT_'+str(imageSequence)+'Zmax'+'.tif'), imagej = True) as tif:                
                        tif.save(self.PMT_image_maxprojection.astype('float32'), compress=0, metadata = {"FocusPos: " : str(self.FocusPos)})

                
            except:
                print('No.{} image failed to generate.'.format(imageSequence))
                        
        
    def generate_tif_name(self, extra_text = "_"):
        
        tif_name = os.path.join(self.scansavedirectory, 'Round'+str(self.RoundWaveformIndex[0])+'_Coords'+str(self.currentCoordsSeq)+ \
                                '_R'+str(self.CurrentPosIndex[0])+'C'+str(self.CurrentPosIndex[1])+ extra_text +'.tif')            
        return tif_name
    
    #----------------------------------------------------------------WatchDog for laser----------------------------------------------------------------------------------
    def Status_watchdog(self, querygap):
        
        while True:
            if self.watchdog_flag == True:
                self.Status_list = self.Laserinstance.QueryStatus()
                time.sleep(querygap)
            else:
                print('Watchdog stopped')
                time.sleep(querygap)