class DAQ_Move_PI(DAQ_Move_base): """ Plugin using the Pi wrapper shipped with new hardware. It is compatible with : DLLDEVICES = { 'PI_GCS2_DLL': ['C-413', 'C-663.11', 'C-863.11', 'C-867', 'C-877', 'C-884', 'C-885', 'C-887', 'C-891', 'E-517', 'E-518', 'E-545', 'E-709', 'E-712', 'E-723', 'E-725', 'E-727', 'E-753', 'E-754', 'E-755', 'E-852B0076', 'E-861', 'E-870', 'E-871', 'E-873', 'C-663.12'], 'C7XX_GCS_DLL': ['C-702', ], 'C843_GCS_DLL': ['C-843', ], 'C848_DLL': ['C-848', ], 'C880_DLL': ['C-880', ], 'E816_DLL': ['E-621', 'E-625', 'E-665', 'E-816', 'E816', ], 'E516_DLL': ['E-516', ], 'PI_Mercury_GCS_DLL': ['C-663.10', 'C-863.10', 'MERCURY', 'MERCURY_GCS1', ], 'PI_HydraPollux_GCS2_DLL': ['HYDRA', 'POLLUX', 'POLLUX2', 'POLLUXNT', ], 'E7XX_GCS_DLL': ['DIGITAL PIEZO CONTROLLER', 'E-710', 'E-761', ], 'HEX_GCS_DLL': ['HEXAPOD', 'HEXAPOD_GCS1', ], 'PI_G_GCS2_DLL': ['UNKNOWN', ], """ _controller_units = 'mm' # dependent on the stage type so to be updated accordingly using self.controller_units = new_unit GCS_path = "" GCS_paths = ["C:\\ProgramData\\PI\\GCSTranslator"] devices = [] #GCS_path = "C:\\Program Files (x86)\\PI\\GCSTranslator" dll_name = '' for GCS_path_tmp in GCS_paths: try: #check for installed dlls flag = False if '64' in platform.machine(): machine = "64" for dll_name_tmp in DLLDEVICES: for file in os.listdir(GCS_path_tmp): if dll_name_tmp in file and '.dll' in file and machine in file: dll_name = file flag = True if flag: break if flag: break gcs_device = GCSDevice(gcsdll=os.path.join(GCS_path_tmp, dll_name)) devices = gcs_device.EnumerateUSB() GCS_path = GCS_path_tmp except Exception as e: pass import serial.tools.list_ports as list_ports devices.extend([str(port) for port in list(list_ports.comports())]) is_multiaxes = False stage_names = [] params = [{ 'title': 'GCS2 library:', 'name': 'gcs_lib', 'type': 'browsepath', 'value': os.path.join(GCS_path_tmp, dll_name), 'filetype': True }, { 'title': 'Connection_type:', 'name': 'connect_type', 'type': 'list', 'value': 'USB', 'values': ['USB', 'TCP/IP', 'RS232'] }, { 'title': 'Devices:', 'name': 'devices', 'type': 'list', 'values': devices }, { 'title': 'Daisy Chain Options:', 'name': 'dc_options', 'type': 'group', 'children': [{ 'title': 'Use Daisy Chain:', 'name': 'is_daisy', 'type': 'bool', 'value': False }, { 'title': 'Is master?:', 'name': 'is_daisy_master', 'type': 'bool', 'value': False }, { 'title': 'Daisy Master Id:', 'name': 'daisy_id', 'type': 'int' }, { 'title': 'Daisy Devices:', 'name': 'daisy_devices', 'type': 'list' }, { 'title': 'Index in chain:', 'name': 'index_in_chain', 'type': 'int', 'enabled': True }] }, { 'title': 'Use Joystick:', 'name': 'use_joystick', 'type': 'bool', 'value': False }, { 'title': 'Closed loop?:', 'name': 'closed_loop', 'type': 'bool', 'value': True }, { 'title': 'Controller ID:', 'name': 'controller_id', 'type': 'str', 'value': '', 'readonly': True }, { 'title': 'Stage address:', 'name': 'axis_address', 'type': 'list' }, { 'title': 'MultiAxes:', 'name': 'multiaxes', 'type': 'group', 'visible': is_multiaxes, 'children': [ { 'title': 'is Multiaxes:', 'name': 'ismultiaxes', 'type': 'bool', 'value': is_multiaxes, 'default': False }, { 'title': 'Status:', 'name': 'multi_status', 'type': 'list', 'value': 'Master', 'values': ['Master', 'Slave'] }, { 'title': 'Axis:', 'name': 'axis', 'type': 'list', 'values': stage_names }, ] }] + comon_parameters def __init__(self, parent=None, params_state=None): super(DAQ_Move_PI, self).__init__(parent, params_state) self.settings.child(('epsilon')).setValue(0.01) self.is_referencing_function = True def commit_settings(self, param): """ | Activate any parameter changes on the PI_GCS2 hardware. | | Called after a param_tree_changed signal from DAQ_Move_main. =============== ================================ ======================== **Parameters** **Type** **Description** *param* instance of pyqtgraph Parameter The parameter to update =============== ================================ ======================== See Also -------- daq_utils.ThreadCommand, DAQ_Move_PI.enumerate_devices """ try: if param.name() == 'gcs_lib': try: self.controller.CloseConnection() except Exception as e: self.emit_status( ThreadCommand("Update_Status", [getLineInfo() + str(e), 'log'])) self.ini_device() elif param.name() == 'connect_type': self.enumerate_devices() elif param.name() == 'use_joystick': axes = self.controller.axes for ind, ax in enumerate(axes): try: if param.value(): res = self.controller.JAX(1, ind + 1, ax) res = self.controller.JON(ind + 1, True) else: self.controller.JON(ind + 1, False) except Exception as e: pass pass elif param.name() == 'axis_address': self.settings.child(('closed_loop')).setValue( self.controller.qSVO(param.value())[param.value()]) self.set_referencing( self.settings.child(('axis_address')).value()) elif param.name() == 'closed_loop': axe = self.settings.child(('axis_address')).value() if self.controller.qSVO(axe)[axe] != self.settings.child( ('closed_loop')).value(): self.controller.SVO(axe, param.value()) except Exception as e: self.emit_status( ThreadCommand("Update_Status", [getLineInfo() + str(e), 'log'])) def enumerate_devices(self): """ Enumerate PI_GCS2 devices from the connection type. Returns ------- string list The list of the devices port. See Also -------- daq_utils.ThreadCommand """ try: devices = [] if self.settings.child(('connect_type')).value() == 'USB': devices = self.controller.EnumerateUSB() elif self.settings.child(('connect_type')).value() == 'TCP/IP': devices = self.controller.EnumerateTCPIPDevices() elif self.settings.child(('connect_type')).value() == 'RS232': devices = [ str(port) for port in list(self.list_ports.comports()) ] self.settings.child(('devices')).setLimits(devices) return devices except Exception as e: self.emit_status( ThreadCommand("Update_Status", [getLineInfo() + str(e), 'log'])) def ini_device(self): """ load the correct dll given the chosen device See Also -------- DAQ_Move_base.close """ try: self.close() except: pass device = self.settings.child(('devices')).value() if self.settings.child( ('connect_type')).value() == 'TCP/IP' or self.settings.child( ('connect_type')).value() == 'RS232': dll_path_tot = self.settings.child(('gcs_lib')).value() else: # device = self.settings.child(('devices')).value().rsplit(' ') # dll = None # flag = False # for dll_tmp in DLLDEVICES: # for dev in DLLDEVICES[dll_tmp]: # for d in device: # if (d in dev or dev in d) and d != '': # res=self.check_dll_exist(dll_tmp) # if res[0]: # dll = res[1] # flag = True # break # if flag: # break # if flag: # break # # if dll is None: # raise Exception('No valid dll found for the given device') # dll_path = os.path.split(self.settings.child(('gcs_lib')).value())[0] # dll_path_tot = os.path.join(dll_path,dll) #dll_name = get_dll_name(self.settings.child(('devices')).value()) #dll_path_tot = get_dll_path(dll_name) #self.settings.child(('gcs_lib')).setValue(dll_path_tot) dll_path_tot = self.settings.child(('gcs_lib')).value() self.controller = GCSDevice(gcsdll=dll_path_tot) def check_dll_exist(self, dll_name): files = os.listdir( os.path.split(self.settings.child(('gcs_lib')).value())[0]) machine = '' if '64' in platform.machine(): machine = '64' res = (False, '') for file in files: if 'dll' in file and machine in file and dll_name in file: res = (True, file) return res def ini_stage(self, controller=None): """ Initialize the controller and stages (axes) with given parameters. =============== =========================================== ========================================================================================== **Parameters** **Type** **Description** *controller* instance of the specific controller object If defined this hardware will use it and will not initialize its own controller instance =============== =========================================== ========================================================================================== Returns ------- Easydict dictionnary containing keys: * *info* : string displaying various info * *controller*: instance of the controller object in order to control other axes without the need to init the same controller twice * *stage*: instance of the stage (axis or whatever) object * *initialized*: boolean indicating if initialization has been done corretly See Also -------- DAQ_Move_PI.set_referencing, daq_utils.ThreadCommand """ try: device = "" # initialize the stage and its controller status # controller is an object that may be passed to other instances of DAQ_Move_Mock in case # of one controller controlling multiaxes self.status.update( edict(info="", controller=None, initialized=False)) #check whether this stage is controlled by a multiaxe controller (to be defined for each plugin) # if mutliaxes then init the controller here if Master state otherwise use external controller if self.settings.child( 'multiaxes', 'ismultiaxes').value() and self.settings.child( 'multiaxes', 'multi_status').value() == "Slave": if controller is None: raise Exception( 'no controller has been defined externally while this axe is a slave one' ) else: self.controller = controller else: #Master stage self.ini_device( ) #create a fresh and new instance of GCS device (in case multiple instances of DAQ_MOVE_PI are opened) device = self.settings.child(('devices')).value() if not self.settings.child( 'dc_options', 'is_daisy').value(): #simple connection if self.settings.child(('connect_type')).value() == 'USB': self.controller.ConnectUSB(device) elif self.settings.child( ('connect_type')).value() == 'TCP/IP': self.controller.ConnectTCPIPByDescription(device) elif self.settings.child( ('connect_type')).value() == 'RS232': self.controller.ConnectRS232( int(device[3:]) ) #in this case device is a COM port, and one should use 1 for COM1 for instance else: #one use a daisy chain connection with a master device and slaves if self.settings.child( 'dc_options', 'is_daisy_master').value(): #init the master if self.settings.child( ('connect_type')).value() == 'USB': dev_ids = self.controller.OpenUSBDaisyChain(device) elif self.settings.child( ('connect_type')).value() == 'TCP/IP': dev_ids = self.controller.OpenTCPIPDaisyChain( device) elif self.settings.child( ('connect_type')).value() == 'RS232': dev_ids = self.controller.OpenRS232DaisyChain( int(device[3:]) ) #in this case device is a COM port, and one should use 1 for COM1 for instance self.settings.child('dc_options', 'daisy_devices').setLimits(dev_ids) self.settings.child('dc_options', 'daisy_id').setValue( self.controller.dcid) self.controller.ConnectDaisyChainDevice( self.settings.child('dc_options', 'index_in_chain').value() + 1, self.settings.child('dc_options', 'daisy_id').value()) self.settings.child( ('controller_id')).setValue(self.controller.qIDN()) self.settings.child( ('axis_address')).setLimits(self.controller.axes) self.set_referencing(self.controller.axes[0]) #check servo status: self.settings.child(('closed_loop')).setValue( self.controller.qSVO( self.controller.axes[0])[self.controller.axes[0]]) self.status.controller = self.controller self.status.info = "connected on device:{} /".format( device) + self.controller.qIDN() self.status.controller = self.controller self.status.initialized = True return self.status except Exception as e: self.emit_status( ThreadCommand('Update_Status', [getLineInfo() + str(e), 'log'])) self.status.info = getLineInfo() + str(e) self.status.initialized = False return self.status def is_referenced(self, axe): """ Return the referencement statement from the hardware device. ============== ========== ============================================ **Parameters** **Type** **Description** *axe* string Representing a connected axe on controller ============== ========== ============================================ Returns ------- ??? """ try: if self.controller.HasqFRF(): return self.controller.qFRF(axe)[axe] else: return False except: return False def set_referencing(self, axes): """ Set the referencement statement into the hardware device. ============== ============== =========================================== **Parameters** **Type** **Description** *axes* string list Representing connected axes on controller ============== ============== =========================================== """ try: if type(axes) is not list: axes = [axes] for axe in axes: #set referencing mode if type(axe) is str: if self.is_referenced(axe): if self.controller.HasRON(): self.controller.RON(axe, True) self.controller.FRF(axe) except Exception as e: self.emit_status( ThreadCommand('Update_Status', [ getLineInfo() + str(e) + " / Referencing not enabled with this dll", 'log' ])) def close(self): """ close the current instance of PI_GCS2 instrument. """ if not self.settings.child('dc_options', 'is_daisy').value(): #simple connection self.controller.CloseConnection() else: self.controller.CloseDaisyChain() def stop_motion(self): """ See Also -------- DAQ_Move_base.move_done """ self.controller.StopAll() self.move_done() def check_position(self): """ Get the current hardware position with scaling conversion of the PI_GCS2 instrument provided by get_position_with_scaling See Also -------- DAQ_Move_base.get_position_with_scaling, daq_utils.ThreadCommand """ self.set_referencing(self.settings.child(('axis_address')).value()) pos_dict = self.controller.qPOS( self.settings.child(('axis_address')).value()) pos = pos_dict[self.settings.child(('axis_address')).value()] pos = self.get_position_with_scaling(pos) self.current_position = pos self.emit_status(ThreadCommand('check_position', [pos])) return pos def move_Abs(self, position): """ Make the hardware absolute move of the PI_GCS2 instrument from the given position after thread command signal was received in DAQ_Move_main. =============== ========= ======================= **Parameters** **Type** **Description** *position* float The absolute position =============== ========= ======================= See Also -------- DAQ_Move_PI.set_referencing, DAQ_Move_base.set_position_with_scaling, DAQ_Move_base.poll_moving """ position = self.check_bound(position) self.target_position = position position = self.set_position_with_scaling(position) out = self.controller.MOV( self.settings.child(('axis_address')).value(), position) self.poll_moving() def move_Rel(self, position): """ Make the hardware relative move of the PI_GCS2 instrument from the given position after thread command signal was received in DAQ_Move_main. =============== ========= ======================= **Parameters** **Type** **Description** *position* float The absolute position =============== ========= ======================= See Also -------- DAQ_Move_base.set_position_with_scaling, DAQ_Move_PI.set_referencing, DAQ_Move_base.poll_moving """ position = self.check_bound(self.current_position + position) - self.current_position self.target_position = position + self.current_position position = self.set_position_relative_with_scaling(position) if self.controller.HasMVR(): out = self.controller.MVR( self.settings.child(('axis_address')).value(), position) else: self.move_Abs(self.target_position) self.poll_moving() def move_Home(self): """ See Also -------- DAQ_Move_PI.set_referencing, DAQ_Move_base.poll_moving """ self.set_referencing(self.settings.child(('axis_address')).value()) if self.controller.HasGOH(): self.controller.GOH(self.settings.child(('axis_address')).value()) elif self.controller.HasFRF(): self.controller.FRF(self.settings.child(('axis_address')).value()) else: self.move_Abs(0) self.poll_moving()
class PIMotorStage(Base, MotorInterface): """ Class representing the PI 3 axis positioning motor stage Example config for copy-paste: pi_stage: module.Class: 'motor.motor_pi_3axis_stage.PIMotorStage' serialnumber_master: '0019550121' first_axis_controllername: 'C-863' second_axis_controllername: 'C-863' third_axis_controllername: 'C-863' first_axis_label: 'x' second_axis_label: 'y' third_axis_label: 'z' first_axis_daisychain_id: 2 # number of the device in the daisy chain (sorted by increasing serial number of the controller) second_axis_daisychain_id: 3 third_axis_daisychain_id: 1 """ _serialnum_master = ConfigOption('serialnumber_master', missing='error') _first_axis_controllername = ConfigOption('first_axis_controllername', missing='error') _second_axis_controllername = ConfigOption('second_axis_controllername', missing='error') _third_axis_controllername = ConfigOption('third_axis_controllername', missing='error') _first_axis_label = ConfigOption('first_axis_label', missing='error') _second_axis_label = ConfigOption('second_axis_label', missing='error') _third_axis_label = ConfigOption('third_axis_label', missing='error') _first_axis_daisychain_id = ConfigOption('first_axis_daisychain_id', missing='error') _second_axis_daisychain_id = ConfigOption('second_axis_daisychain_id', missing='error') _third_axis_daisychain_id = ConfigOption('third_axis_daisychain_id', missing='error') def __init__(self, config, **kwargs): super().__init__(config=config, **kwargs) # def __init__(self, *args, **kwargs): # super().__init__() def on_activate(self): try: self.first_axis_label = self._first_axis_label self.second_axis_label = self._second_axis_label self.third_axis_label = self._third_axis_label # open the daisy chain connection self.pidevice_1st_axis = GCSDevice( self._first_axis_controllername ) # 1st axis controller # master device self.pidevice_2nd_axis = GCSDevice( self._second_axis_controllername) # 2nd axis controller self.pidevice_3rd_axis = GCSDevice( self._third_axis_controllername) # 3rd axis controller self.pidevice_1st_axis.OpenUSBDaisyChain( description=self._serialnum_master) self.daisychainid = self.pidevice_1st_axis.dcid print(f'Daisychainid: {self.daisychainid}') # controllers are ordered with increasing serial number in the daisy chain # this is why z is connected as first # do we need to programmatically sort by nth_axis_daisychain id ?? self.pidevice_3rd_axis.ConnectDaisyChainDevice( self._third_axis_daisychain_id, self.daisychainid) # SN 019550119 self.pidevice_1st_axis.ConnectDaisyChainDevice( self._first_axis_daisychain_id, self.daisychainid) # SN 019550121 self.pidevice_2nd_axis.ConnectDaisyChainDevice( self._second_axis_daisychain_id, self.daisychainid) # SN 019550124 print('\n{}:\n{}'.format( self.pidevice_1st_axis.GetInterfaceDescription(), self.pidevice_1st_axis.qIDN())) print('\n{}:\n{}'.format( self.pidevice_2nd_axis.GetInterfaceDescription(), self.pidevice_2nd_axis.qIDN())) print('\n{}:\n{}'.format( self.pidevice_3rd_axis.GetInterfaceDescription(), self.pidevice_3rd_axis.qIDN())) # initialization of all axes print('Initializing PI stage ...') # servo on pitools.startup(self.pidevice_1st_axis) pitools.startup(self.pidevice_2nd_axis) pitools.startup(self.pidevice_3rd_axis) print('Please wait ... ') # the IDs are needed to address the axes in the dll functions self.first_axis_ID = self.pidevice_1st_axis.axes[ 0] # each controller is connected to one stage; so just take the first element # print(self.first_axis_ID) self.second_axis_ID = self.pidevice_2nd_axis.axes[0] # print(self.second_axis_ID) self.third_axis_ID = self.pidevice_3rd_axis.axes[0] # print(self.third_axis_ID) self.calibrate() # RON: # FNL: fast move to negative limit # self.pidevice_1st_axis.RON(self.first_axis_ID, values=1) # self.pidevice_1st_axis.FNL(self.first_axis_ID) # self.pidevice_2nd_axis.RON(self.second_axis_ID, values=1) # self.pidevice_2nd_axis.FNL(self.second_axis_ID) # self.pidevice_3rd_axis.RON(self.third_axis_ID, values=1) # self.pidevice_3rd_axis.FNL(self.third_axis_ID) # pitools.waitontarget(self.pidevice_1st_axis, axes=self.first_axis_ID) # pitools.waitontarget(self.pidevice_2nd_axis, axes=self.second_axis_ID) # pitools.waitontarget(self.pidevice_3rd_axis, axes=self.third_axis_ID) except Exception as e: self.log.error( f'Physik Instrumente 3-axes stage: Connection failed: {e}. Check if device is switched on.' ) def on_deactivate(self): """ Required deactivation steps """ # set position (0, 0, 0) # first move z to default position and wait until reached self.pidevice_3rd_axis.MOV(self.third_axis_ID, 0.0) pitools.waitontarget(self.pidevice_3rd_axis, axes=self.third_axis_ID) # when z is at safety position, xy move can be done self.pidevice_1st_axis.MOV(self.first_axis_ID, 0.0) self.pidevice_2nd_axis.MOV(self.second_axis_ID, 0.0) pitools.waitontarget(self.pidevice_1st_axis, axes=self.first_axis_ID) pitools.waitontarget(self.pidevice_2nd_axis, axes=self.second_axis_ID) self.pidevice_1st_axis.CloseDaisyChain( ) # check if connection always done with controller corresponing to 1st axis self.pidevice_1st_axis.CloseConnection() def get_constraints(self): """ Retrieve the hardware constrains from the motor device. @return dict: dict with constraints for the motor hardware Provides all the constraints for each axis of a motorized stage (like total travel distance, velocity, ...) Each axis has its own dictionary, where the label is used as the identifier throughout the whole module. The dictionaries for each axis are again grouped together in a constraints dictionary in the form {'<label_axis0>': axis0 } where axis0 is again a dict with the possible values defined below. The possible keys in the constraint are defined here in the interface file. If the hardware does not support the values for the constraints, then insert just None. If you are not sure about the meaning, look in other hardware files to get an impression. """ constraints = {} # retrieve information from hardware pos_min_x = self.pidevice_1st_axis.qTMN()[self.first_axis_ID] pos_max_x = self.pidevice_1st_axis.qTMX()[self.first_axis_ID] vel_min_x = 0 # self.pidevice_c863_x.q vel_max_x = 20 # self.pidevice_c863_x.q # need to find the command axis0 = {} axis0[ 'label'] = self.first_axis_label # it is very crucial that this label coincides with the label set in the config. axis0['unit'] = 'm' # the SI units, only possible m or degree axis0['ramp'] = None # do we need this ? axis0['pos_min'] = pos_min_x axis0['pos_max'] = pos_max_x axis0['pos_step'] = pos_max_x axis0['vel_min'] = vel_min_x axis0['vel_max'] = vel_max_x axis0['vel_step'] = 0.01 # can this also be queried ? axis0['acc_min'] = None # do we need this ? axis0['acc_max'] = None axis0['acc_step'] = None # retrieve information from hardware pos_min_y = self.pidevice_2nd_axis.qTMN()[self.second_axis_ID] pos_max_y = self.pidevice_2nd_axis.qTMX()[self.second_axis_ID] vel_min_y = 0 # self.pidevice_c863_y.q vel_max_y = 20 # self.pidevice_c863_y.q # need to find the command axis1 = {} axis1[ 'label'] = self.second_axis_label # it is very crucial that this label coincides with the label set in the config. axis1['unit'] = 'm' # the SI units, only possible m or degree axis1['ramp'] = None # do we need this ? axis1['pos_min'] = pos_min_y axis1['pos_max'] = pos_max_y axis1[ 'pos_step'] = pos_max_y # allow to go directly from low limit to maximum axis1['vel_min'] = vel_min_y axis1['vel_max'] = vel_max_y axis1['vel_step'] = 0.01 # can this also be queried ? axis1['acc_min'] = None # do we need this ? axis1['acc_max'] = None axis1['acc_step'] = None # retrieve information from hardware pos_min_z = self.pidevice_3rd_axis.qTMN()[self.third_axis_ID] pos_max_z = self.pidevice_3rd_axis.qTMX()[self.third_axis_ID] vel_min_z = 0 # self.pidevice_c863_z.q vel_max_z = 20 # self.pidevice_c863_z.q # need to find the command axis2 = {} axis2[ 'label'] = self.third_axis_label # it is very crucial that this label coincides with the label set in the config. axis2['unit'] = 'm' # the SI units, only possible m or degree axis2['ramp'] = None # do we need this ? axis2['pos_min'] = pos_min_z axis2['pos_max'] = pos_max_z axis2[ 'pos_step'] = pos_max_z # can this also be queried from the hardware ? if not just set a reasonable value axis2['vel_min'] = vel_min_z axis2['vel_max'] = vel_max_z axis2['vel_step'] = 0.01 # can this also be queried ? axis2['acc_min'] = None # do we need this ? axis2['acc_max'] = None axis2['acc_step'] = None # assign the parameter container for each axis to a name which will identify it constraints[axis0['label']] = axis0 constraints[axis1['label']] = axis1 constraints[axis2['label']] = axis2 return constraints def move_rel(self, param_dict): """ Moves stage in given direction (relative movement) @param dict param_dict: dictionary, which passes all the relevant parameters, which should be changed. Usage: {'axis_label': <the-abs-pos-value>}. 'axis_label' must correspond to a label given to one of the axis. A smart idea would be to ask the position after the movement. @return int: error code (0:OK, -1:error) """ constraints = self.get_constraints() cur_pos = self.get_pos() for key, value in param_dict.items( ): # param_dict has the format {'x': 20, 'y': 0, 'z': 10} for example if key == self.first_axis_label: if value <= constraints[ self.first_axis_label]['pos_step'] and constraints[ self.first_axis_label]['pos_min'] <= cur_pos[ self.first_axis_label] + value <= constraints[ self.first_axis_label]['pos_max']: self.pidevice_1st_axis.MVR(self.first_axis_ID, value) # pitools.waitontarget(self.pidevice_1st_axis, axes=self.first_axis_ID) else: print( 'Target value not in allowed range. Relative movement not done.' ) elif key == self.second_axis_label: if value <= constraints[ self.second_axis_label]['pos_step'] and constraints[ self.second_axis_label]['pos_min'] <= cur_pos[ self.second_axis_label] + value <= constraints[ self.second_axis_label]['pos_max']: self.pidevice_2nd_axis.MVR(self.second_axis_ID, value) # pitools.waitontarget(self.pidevice_2nd_axis, axes=self.second_axis_ID) else: print( 'Target value not in allowed range. Relative movement not done.' ) elif key == self.third_axis_label: if value <= constraints[ self.third_axis_label]['pos_step'] and constraints[ self.third_axis_label]['pos_min'] <= cur_pos[ self.third_axis_label] + value <= constraints[ self.third_axis_label]['pos_max']: self.pidevice_3rd_axis.MVR(self.third_axis_ID, value) # pitools.waitontarget(self.pidevice_3rd_axis, axes=self.third_axis_ID) else: print( 'Target value not in allowed range. Relative movement not done.' ) else: print('Given axis not available.') # handle the return statement def move_abs(self, param_dict): """ Moves stage to absolute position (absolute movement) @param dict param_dict: dictionary, which passes all the relevant parameters, which should be changed. Usage: {'axis_label': <the-abs-pos-value>}. 'axis_label' must correspond to a label given to one of the axis. @return int: error code (0:OK, -1:error) """ constraints = self.get_constraints() for key, value in param_dict.items(): if key == self.first_axis_label: if constraints[self.first_axis_label][ 'pos_min'] <= value <= constraints[ self.first_axis_label]['pos_max']: self.pidevice_1st_axis.MOV(self.first_axis_ID, value) # pitools.waitontarget(self.pidevice_1st_axis, axes=self.first_axis_ID) else: print( 'Target value not in allowed range. Absolute movement not done.' ) elif key == self.second_axis_label: if constraints[self.second_axis_label][ 'pos_min'] <= value <= constraints[ self.second_axis_label]['pos_max']: self.pidevice_2nd_axis.MOV(self.second_axis_ID, value) # pitools.waitontarget(self.pidevice_2nd_axis, axes=self.second_axis_ID) else: print( 'Target value not in allowed range. Absolute movement not done.' ) elif key == self.third_axis_label: if constraints[self.third_axis_label][ 'pos_min'] <= value <= constraints[ self.third_axis_label]['pos_max']: self.pidevice_3rd_axis.MOV(self.third_axis_ID, value) # pitools.waitontarget(self.pidevice_3rd_axis, axes=self.third_axis_ID) else: print( 'Target value not in allowed range. Absolute movement not done.' ) else: print('Given axis not available.') # handle the return statement def abort(self): """ Stops movement of the stage @return int: error code (0:OK, -1:error) """ self.pidevice_1st_axis.HLT( noraise=True) # noraise option silences GCSerror 10 self.pidevice_2nd_axis.HLT(noraise=True) self.pidevice_3rd_axis.HLT(noraise=True) print('Movement aborted.') # handle return value def get_pos(self, param_list=None): """ Gets current position of the stage arms @param list param_list: optional, if a specific position of an axis is desired, then the labels of the needed axis should be passed in the param_list. If nothing is passed, then from each axis the position is asked. @return dict: with keys being the axis labels and item the current position. """ # # if stage is moving, wait until movement done before reading position # pitools.waitontarget(self.pidevice_1st_axis, axes=self.first_axis_ID) # pitools.waitontarget(self.pidevice_2nd_axis, axes=self.second_axis_ID) # pitools.waitontarget(self.pidevice_3rd_axis, axes=self.third_axis_ID) if not param_list: x_pos = self.pidevice_1st_axis.qPOS( )[self. first_axis_ID] # qPOS returns OrderedDict, we just need the value for the single axis y_pos = self.pidevice_2nd_axis.qPOS()[self.second_axis_ID] z_pos = self.pidevice_3rd_axis.qPOS()[self.third_axis_ID] positions = [x_pos, y_pos, z_pos] keys = [ self.first_axis_label, self.second_axis_label, self.third_axis_label ] pos_dict = dict(zip(keys, positions)) return pos_dict else: pos_dict = {} for item in param_list: if item == self.first_axis_label: x_pos = self.pidevice_1st_axis.qPOS()[self.first_axis_ID] pos_dict[item] = x_pos elif item == self.second_axis_label: y_pos = self.pidevice_2nd_axis.qPOS()[self.second_axis_ID] pos_dict[item] = y_pos elif item == self.third_axis_label: z_pos = self.pidevice_3rd_axis.qPOS()[self.third_axis_ID] pos_dict[item] = z_pos else: print('Given axis not available.') return pos_dict # IsControllerReady does eventually not give the right information. Better replace by an information if stage moving / not moving # use qONT query ? on target ? def get_status(self, param_list=None): """ Get the status of the position @param list param_list: optional, if a specific status of an axis is desired, then the labels of the needed axis should be passed in the param_list. If nothing is passed, then from each axis the status is asked. @return dict: with the axis label as key and the on target status as value. """ if not param_list: x_status = self.pidevice_1st_axis.qONT()[self.first_axis_ID] y_status = self.pidevice_2nd_axis.qONT()[self.second_axis_ID] z_status = self.pidevice_3rd_axis.qONT()[self.third_axis_ID] on_target = [x_status, y_status, z_status] keys = [ self.first_axis_label, self.second_axis_label, self.third_axis_label ] status_dict = dict(zip(keys, on_target)) return status_dict else: status_dict = {} for item in param_list: if item == self.first_axis_label: x_status = self.pidevice_1st_axis.qONT()[ self.first_axis_ID] status_dict[item] = x_status elif item == self.second_axis_label: y_status = self.pidevice_2nd_axis.qONT()[ self.second_axis_ID] status_dict[item] = y_status elif item == self.third_axis_label: z_status = self.pidevice_3rd_axis.qONT()[ self.third_axis_ID] status_dict[item] = z_status else: print('Given axis not available.') return status_dict def calibrate(self, param_list=None): """ Calibrates the stage. @param dict param_list: param_list: optional, if a specific calibration of an axis is desired, then the labels of the needed axis should be passed in the param_list. If nothing is passed, then all connected axis will be calibrated. @return int: error code (0:OK, -1:error) After calibration the stage moves to home position which will be the zero point for the passed axis. The calibration procedure will be different for each stage. """ if not param_list: # 3rd axis is typically z. Calibrate and move first z to negative limit self.pidevice_3rd_axis.RON(self.third_axis_ID, values=1) self.pidevice_3rd_axis.FNL(self.third_axis_ID) pitools.waitontarget(self.pidevice_3rd_axis, axes=self.third_axis_ID) self.pidevice_1st_axis.RON(self.first_axis_ID, values=1) self.pidevice_1st_axis.FNL(self.first_axis_ID) self.pidevice_2nd_axis.RON(self.second_axis_ID, values=1) self.pidevice_2nd_axis.FNL(self.second_axis_ID) pitools.waitontarget(self.pidevice_1st_axis, axes=self.first_axis_ID) pitools.waitontarget(self.pidevice_2nd_axis, axes=self.second_axis_ID) else: for item in param_list: if item == self.first_axis_label: self.pidevice_1st_axis.RON(self.first_axis_ID, values=1) self.pidevice_1st_axis.FNL(self.first_axis_ID) pitools.waitontarget(self.pidevice_1st_axis, axes=self.first_axis_ID) elif item == self.second_axis_label: self.pidevice_2nd_axis.RON(self.second_axis_ID, values=1) self.pidevice_2nd_axis.FNL(self.second_axis_ID) pitools.waitontarget(self.pidevice_2nd_axis, axes=self.second_axis_ID) elif item == self.third_axis_label: self.pidevice_3rd_axis.RON(self.third_axis_ID, values=1) self.pidevice_3rd_axis.FNL(self.third_axis_ID) pitools.waitontarget(self.pidevice_3rd_axis, axes=self.third_axis_ID) else: print('Given axis not available.') def get_velocity(self, param_list=None): """ Gets the current velocity for all connected axes. @param dict param_list: optional, if a specific velocity of an axis is desired, then the labels of the needed axis should be passed as the param_list. If nothing is passed, then from each axis the velocity is asked. @return dict : with the axis label as key and the velocity as item. """ if not param_list: x_vel = self.pidevice_1st_axis.qVEL( )[self. first_axis_ID] # qVEL returns OrderedDict, we just need the value for the single axis y_vel = self.pidevice_2nd_axis.qVEL()[self.second_axis_ID] z_vel = self.pidevice_3rd_axis.qVEL()[self.third_axis_ID] velocity = [x_vel, y_vel, z_vel] keys = [ self.first_axis_label, self.second_axis_label, self.third_axis_label ] vel_dict = dict(zip(keys, velocity)) return vel_dict else: vel_dict = {} for item in param_list: if item == self.first_axis_label: x_vel = self.pidevice_1st_axis.qVEL()[self.first_axis_ID] vel_dict[item] = x_vel elif item == self.second_axis_label: y_vel = self.pidevice_2nd_axis.qVEL()[self.second_axis_ID] vel_dict[item] = y_vel elif item == self.third_axis_label: z_vel = self.pidevice_3rd_axis.qVEL()[self.third_axis_ID] vel_dict[item] = z_vel else: print('Given axis not available.') return vel_dict def set_velocity(self, param_dict): """ Write new value for velocity. @param dict param_dict: dictionary, which passes all the relevant parameters, which should be changed. Usage: {'axis_label': <the-velocity-value>}. 'axis_label' must correspond to a label given to one of the axis. @return int: error code (0:OK, -1:error) """ constraints = self.get_constraints() for key, value in param_dict.items( ): # param_dict has the format {'x': 20, 'y': 0, 'z': 10} for example if key == self.first_axis_label: if constraints[self.first_axis_label][ 'vel_min'] <= value <= constraints[ self.first_axis_label]['vel_max']: self.pidevice_1st_axis.VEL(self.first_axis_ID, value) else: print( 'Target value not in allowed range. Velocity not set.') elif key == self.second_axis_label: if constraints[self.second_axis_label][ 'vel_min'] <= value <= constraints[ self.second_axis_label]['vel_max']: self.pidevice_2nd_axis.VEL(self.second_axis_ID, value) else: print( 'Target value not in allowed range. Velocity not set.') elif key == self.third_axis_label: if constraints[self.third_axis_label][ 'vel_min'] <= value <= constraints[ self.third_axis_label]['vel_max']: self.pidevice_3rd_axis.VEL(self.third_axis_ID, value) else: print( 'Target value not in allowed range. Velocity not set.') else: print('Given axis not available.')