Source code for PyExpLabSys.drivers.specs_iqe11

# pylint: disable=C0301,R0904, C0103

"""
Self contained module to run a SPECS sputter gun including fall-back text gui
"""

import serial
import time
import threading
import curses


[docs]class CursesTui(threading.Thread): """ Defines a fallback text-gui for the sputter gun. """
[docs] def __init__(self, sputtergun): threading.Thread.__init__(self) self.sg = sputtergun self.screen = curses.initscr() curses.noecho() curses.cbreak() curses.curs_set(False) self.screen.keypad(1) self.screen.nodelay(1) self.time = time.time()
[docs] def run(self): while True: self.screen.addstr(3, 2, 'Sputter Gun Control') if self.sg.status['degas']: self.screen.addstr(4, 2, "Degassing") if self.sg.status['remote']: self.screen.addstr(5, 2, "Remote control") if self.sg.status['standby']: self.screen.addstr(6, 2, "Device status: Standby ") if self.sg.status['operate']: self.screen.addstr(6, 2, "Device status: Operate! ") try: self.screen.addstr( 9, 2, "Temperature, electronics: {0:.0f}C ".format( self.sg.status['temperature'] ), ) self.screen.addstr( 10, 2, "Sputter Current: {0:.4f}mA ".format( self.sg.status['sputter_current'] ), ) self.screen.addstr( 11, 2, "Filament bias: {0:.3f}V ".format( self.sg.status['filament_bias'] ), ) self.screen.addstr( 12, 2, "Filament Current: {0:.2f}A ".format( self.sg.status['filament_current'] ), ) self.screen.addstr( 13, 2, "Emission current: {0:.4f}mA ".format( self.sg.status['emission_current'] ), ) self.screen.addstr( 14, 2, "Acceleration Voltage: {0:.2f}V ".format( self.sg.status['accel_voltage'] ), ) except ValueError: self.screen.addstr( 9, 2, "Temperature, electronics: - " ) self.screen.addstr( 10, 2, "Sputter Current: - " ) self.screen.addstr( 11, 2, "Filament bias: - " ) self.screen.addstr( 12, 2, "Filament Current: - " ) self.screen.addstr( 13, 2, "Emission current: - " ) self.screen.addstr( 14, 2, "Acceleration Voltage: - " ) # self.screen.addstr(16, 2, "Latest error message: " + self.sg.status['error']) self.screen.addstr( 17, 2, "Runtime: {0:.0f}s ".format(time.time() - self.time) ) self.screen.addstr(18, 2, 'q: quit, s: standby, o: operate') n = self.screen.getch() if n == ord('q'): self.sg.running = False if n == ord('s'): self.sg.goto_standby = True if n == ord('o'): self.sg.goto_operate = True self.screen.refresh() time.sleep(1)
[docs] def stop(self): """ Cleanup the terminal """ curses.nocbreak() self.screen.keypad(0) curses.echo() curses.endwin()
[docs]class Puiqe11(threading.Thread): """ Driver for ion sputter guns from SPECS """
[docs] def __init__(self, simulate=False): """Initialize module Establish serial connection and create status variable to expose the status for the instrument for the various gui's """ threading.Thread.__init__(self) self.simulate = simulate self.f = serial.Serial('/dev/ttyS0', 1200, timeout=0.25) self.f.write('e0' + '\r') # Echo off time.sleep(1) ok = self.f.read(self.f.inWaiting()) if ok.find('OK') > -1: pass else: if self.simulate is not True: print('ERROR!!!') self.status = {} # Hold parameters to be accecible by gui self.status['hv'] = None self.status['standby'] = None self.status['operate'] = None self.status['degas'] = None self.status['remote'] = None self.status['error'] = '' self.status['temperature'] = None self.status['sputter_current'] = None self.status['filament_bias'] = None self.status['filament_current'] = None self.status['emission_current'] = None self.status['accel_voltage'] = None self.running = True self.goto_standby = False self.goto_operate = False
# self.update_status()
[docs] def comm(self, command): """Communication with the instrument Implements the synatx need to send commands to instrument including handling carrige returns and extra lines of 'OK' and other pecularities of the instrument. :param command: The command to send :type command: str :return: The reply to the command striped for protocol technicalities :rtype: str """ n = self.f.inWaiting() if n > 1: print('Error') else: self.f.read(n) self.f.write(command + '\r') time.sleep(0.1) reply = self.f.readline() self.f.read(1) # Empty buffer for extra newline time.sleep(0.1) ok_reply = self.f.readline() # Wait for OK cr_count = reply.count('\r') # Check that no old commands is still in buffer and that the reply # is actually intended for the requested parameter cr_check = cr_count == 1 command_check = reply[0 : len(command) - 1] == command.strip('?') ok_check = ok_reply.find('OK') > -1 if cr_check and command_check and ok_check: echo_length = len(command) return_string = reply[echo_length:] elif command == 'os': return_string = reply else: if self.simulate is False: return_string = 'Communication error!' else: return 1 return return_string
[docs] def read_sputter_current(self): """Read the sputter current. Unit mA :return: The sputter current :rtype: float """ reply = self.comm('eni?') try: value = float(reply) / 1000 except ValueError: self.status['error'] = reply value = None return value
[docs] def read_filament_voltage(self): """Read the filament voltage. Unit V :return: The filament voltage :rtype: float """ reply = self.comm('fu?') try: value = float(reply) / 100.0 except ValueError: self.status['error'] = reply value = None return value
[docs] def read_filament_current(self): """Read the filament current. Unit A :return: The filament current :rtype: float """ reply = self.comm('fi?') try: value = float(reply) / 10.0 except ValueError: self.status['error'] = reply value = None return value
[docs] def read_emission_current(self): """Read the emission current. Unit mA :return: The emission current :rtype: float """ reply = self.comm('ec?') try: value = float(reply) / 1000 except ValueError: self.status['error'] = reply value = None return value
[docs] def read_acceleration_voltage(self): """Read the acceleration voltage. Unit V :return: The acceleration voltage :rtype: float """ reply = self.comm('ec?') try: value = float(reply) except ValueError: self.status['error'] = reply value = None return value
[docs] def read_temperature_energy_module(self): """Read the temperature of the electronics module This value is not extremely correct, use only as guideline. :return: The temperature :rtype: float """ reply = self.comm('ent?') try: value = float(reply) except ValueError: self.status['error'] = reply value = None return value
[docs] def standby(self): """Set the device on standby The function is not working entirely as intended. TODO: Implement check to see if the device is alrady in standby :return: The direct reply from the device :rtype: str """ reply = self.comm('sb') return reply
[docs] def operate(self): """Set the device in operation mode TODO: This function should only be activated from standby!!! :return: The direct reply from the device :rtype: str """ reply = self.comm('op') return reply
[docs] def remote_enable(self, local=False): """Enable or disable remote mode :param local: If True the device is set to local, otherwise to remote :type local: Boolean :return: The direct reply from the device :rtype: str """ if local: reply = self.comm('lo') else: reply = self.comm('re') return reply
[docs] def update_status(self): """Update the status of the instrument Runs a number of status queries and updates self.status :return: The direct reply from the device :rtype: str """ self.status['temperature'] = self.read_temperature_energy_module() self.status['filament_bias'] = self.read_filament_voltage() self.status['sputter_current'] = self.read_sputter_current() self.status['filament_current'] = self.read_filament_current() self.status['emission_current'] = self.read_emission_current() self.status['accel_voltage'] = self.read_acceleration_voltage() reply = self.comm('os').lower() if self.simulate is not True: hv = None else: hv = False if reply.find('he') > -1: hv = False if reply.find('ha') > -1: hv = True assert hv is True or hv is False self.status['hv'] = hv if reply.find('re') > -1: self.status['remote'] = True else: self.status['remote'] = False if reply.find('sb') > -1: self.status['standby'] = True else: self.status['standby'] = False if reply.find('op') > -1: self.status['operate'] = True else: self.status['operate'] = False #!TODO: Update status to also accept neither operate or standby if reply.find('dg') > -1: self.status['degas'] = True else: self.status['degas'] = False return reply
[docs] def run(self): while self.running: time.sleep(0.5) self.update_status() if self.goto_standby: self.standby() self.goto_operate = False self.goto_standby = False if self.goto_operate: self.operate() self.goto_operate = False
if __name__ == '__main__': sputter = Puiqe11() sputter.start() tui = CursesTui(sputter) tui.daemon = True tui.start() # print('Sputter current: ' + str(sputter.read_sputter_current())) # print('Temperature: ' + str(sputter.read_temperature_energy_module())) # print('Sputter current: ' + str(sputter.read_sputter_current())) # print('Temperature: ' + str(sputter.read_temperature_energy_module())) # print('Filament voltage: ' + str(sputter.read_filament_voltage())) # print('Filament current: ' + str(sputter.read_filament_current())) # print('Emission current: ' + str(sputter.read_emission_current())) # print('Acceleration voltage: ' + str(sputter.read_acceleration_voltage())) # sputter.update_status() # print('Enable:') # print(sputter.remote_enable(local=False)) # print('Status:') # print(sputter.status)