Source code for pylab_ml.thermostreamer.mpi_ta5k

"""Thermostreamer MPI_TA5000.

:Date: |today|
:Author: Semi-ATE <info@Semi-ATE.org>

todo:
   MPI_TA5K send no acknowledge,
   check if this module is running for other Thermostreamer
"""
import time
from enum import Enum
from datetime import datetime
from pylab_ml.common.spinner import spinner
from pylab_ml.base_instrument import logger
from pylab_ml.collate_instrument import Interface
from pylab_ml.thermostreamer.base_thermostreamer import Base_Thermostreamer
from pylab_ml.attributes import create_attributes


[docs] class MPI_TA5K(create_attributes, Base_Thermostreamer): """ Interface to the Thermostreamer MPI_TA5000. :Date: |today| :Author: Semi-ATE <info@Semi-ATE.org> .. image:: ../static/mpi_ta5000.jpg """ # create functions or proberty and wrap it to the inst.funcname: # if state != necessary state -> switch to the necessary state # proberty/function name -> inst.funcname (get,set) , range, call functions # range : None, Enum or range value # call functions: see help in attributes _properties = { 'airtemp': (('TMPA?', None), None, None), 'compressor': (('COOL?', 'COOL '), 'Compressor', {'sac': '_compressor(value)'}), 'dutsensortype': (('DSNS?', 'DSNS '), [0, 4], None), 'dutmode': (('DUTM?', 'DUTM '), [0, 2], None), 'duttemp': (('TMPD?', None), None, None), 'flow': (('FLOW?', 'FLOW '), 'Flow', {'sa': '_wait4flow(value)'}), 'flowrate': (('FLWM?', 'FLWM '), [5, 18], None), 'head': (('HEAD?', 'HEAD '), 'Head', {'sac': '_head(value)'}), 'headlock': (('HDLK?', 'HDLK '), 'Headlock', None), 'llim': (('LLIM?', 'LLIM '), [-150.0, 25.0], None), 'ramp': (('RAMP?', 'RAMP '), [0.0, 99.9], None), 'setn': (('SETN?', 'SETN '), [0-17], None), 'setupfile': (('SFIL?', 'SFIL '), None, None), 'soak': (('SOAK?', 'SOAK '), [0, 9999], None), 'state': (('TECR?', None), None, {'gac': '_trstate(value)'}), 'Temp': (('SETP?', 'SETP '), None, None), 'ulim': (('ULIM?', 'ULIM '), [25.0, 225.0], None), 'window': (('WNDW?', 'WNDW '), [0.1, 9.9], None), 'what': (('WHAT?', None), None, None), } _states = {1: ' at temperature (soak time has elapsed)', 2: ' not at temperature', 4: ' end of test (test time has elapsed', 8: ' end of one cycle', 16: ' end of all cycle', 32: ' stopped cycling ("stop on fail" signal was received', 128: ' datalogging on', } interchoices = [Interface.usbserial, Interface.gpib]
[docs] class Flow(Enum): """Enum for Flow control.""" off = 0 """value for flow off""" on = 1 """value for flow on"""
[docs] class Head(Enum): """Enum for Head control.""" up = 0 """value for head up""" down = 1 """value for head down"""
[docs] class Headlock(Enum): """Enum for Headlock control.""" off = 0.0 on = 1.0
[docs] class Compressor(Enum): """Enum for Compressor control.""" off = 0 on = 1
[docs] def __init__(self, addr=None, interface=None, backend=None, identify=True, instName=None): """Initialise. Args: addr (int): interface address interface (dev_interface.Instrument): gpib, usbserial instName (string): Instance Name from parent. Raises: TimeoutError: timeout after set temp. InvalidInstrumentConnection: something is wrong with the connection. Examples: >>> # Initialization >>> thermo = Thermo(addr=1) # GPIB address more detailed examples: common for MPI_TA5k.py: :download:`examples/thermostreamer/mpi_ta5k <../../../examples/thermostreamer/mpi_ta5k.py>` """ create_attributes.__init__(self) kwargs = {"addr": addr, "interface": interface, "backend": backend, "identify": identify, "instName": instName} Base_Thermostreamer.__init__(self, **kwargs) logger.debug("Class {}".format(self.__class__.__name__)) self.setdefault()
[docs] def setup_inst(self): """Set for instrument settings.""" self.createattributes(self._properties) super().setup_inst() self.__is_initialized = True
[docs] def setdefault(self): """Set for default values.""" logger.measure("{} set default values".format(self.instName)) # self.head=0 # head sould be hold in it' old position self.flow = self.Flow.off self.llim = -85 # lower temperature limit self.ulim = 180 # upper temperature limit self.window = 1.0 # temp window: 0.1 .. 9.9 self.soak = 20 # tsoak time: 0 .. 9999s self.flowrate = 10 # flow rate: 4 .. 18 self.dut_mode = 0 # 0: off (air control) self._setpoint = None self.dutsensortype = 0 # 0: no dut sensor self.get_state()
# ---------------------------------------------------------- # doc strings for _properties, propertie themselve will create form dictionary _properties @property def airtemp(self): """Current air temperture.""" @property def compressor(self): """Set/get compressor on/off. | 'off'(=0, Compressor.off). | 'on'(=1, Compressor.on) """ @property def dutsensortype(self): """Set/get dut sensortype. | 0: no dut sensor | 1: type T thermocouple | 2: type K thermocouple | 3: RTD """ @property def dutmode(self): """Set/get dut mode. | 0: off (air control) | 1: on (dut control) | 2: TC Meter mode """ @property def duttemp(self): """Get current dut temperture.""" @property def flow(self): """Set/get flow. | 'off'(=0, Flow.off). | 'on'(=1, FLow.on). """ @property def flowrate(self): """Set/get air flow rate.""" @property def head(self): """Set/get head. | 'up'(=0, Head.up) | 'down'(=1, Head.down) """ @property def headlock(self): """Set/get head lock. | 'off' (=0, Headlock.off) -> unlock | 'on' (=1, Headlock.on) -> lock """ @property def llim(self): """Set/get lower limit.""" @property def ramp(self): """Set/get ramp rate for the currently selected setpoint.""" @property def setn(self): """Set/get setpoint. | set: select a setpoint to the current setpoint | get: read the current setpoint number """ @property def setupfile(self): """Set/get filename for setup file. | get the filename, | set = load the test setup file with the filename """ @property def soak(self): """Set/get soak time for a given setpoint.""" @property def state(self): """Get temperature status register.""" @property def Temp(self): """Get/set the currently selected setpoint temperatur.""" @property def ulim(self): """Set/get upper limit.""" @property def window(self): """set/get setpoint temperature window.""" # end doc strings for _properties # ---------------------------------------------------------- def _compressor(self, value): self.flow = self.Flow.off def _head(self, value): if self.headlock == self.Headlock.on: logger.error("{}.Head is locked! Can not move Head".format(self.instName)) return self.ATTR_ERROR return None def _wait4flow(self, value): # logging.disable(logging.MEASURE) # if value==self.flow.on and self.head==self.Head.up: self.head=self.Head.down if value == self.Flow.on and float(self.inst.query('HDLK?')) == 0 and self.inst.query('HEAD?') == '0': logger.error("{}.Head is up and not locked! Can not set Flow.on".format(self.instName)) return self.ATTR_ERROR start = time.time() with spinner(): while int(self.inst.query('FLOW?')) != value.value: time.sleep(0.5) if time.time()-start > 20: logger.error("{}.Flow couln't set flow := {}".format(self.instName, value)) return self.ATTR_ERROR def _trstate(self, value): for bit in range(0, 7): mask = value & (1 << bit) if mask > 0 and mask in self._states: print(self._states[mask]) @property def id(self): """Query IDN.""" value = self.inst.query('*IDN?') return value.replace('\r', '').replace('\n', '')
[docs] def get_id(self): """ Get identifikation from Thermostreamer. Returns: string: identifikation. """ self.inst.write('*IDN?') return self.inst.read()
@property def temp(self): """ Set/get DUT sensor or air temperature. - get temperature - or set the currently selected setpoint temperatur and wait until temperture is reached. Args: value (float): set dut sensor or air temperature timeout (int, optional): timeout time. Defaults to 600s. Returns: float: * in Air mode: get main air temperature * in Dut mode: get DUT sensor temperatur * in TC Meter mode: siehe TEMP? in Manual. Example: >>> thermo.temp = -10, 100 # -> set temperatur=-10 and timeout to 100s (default 600s) >>> thermo.temp = 80 # -> set temperatur=80 and timeout is default = 600s >>> print(thermo.temp) # get temperature """ return float(self.inst.query('TEMP?')) @temp.setter def temp(self, value, timeout=600): if isinstance(value, tuple): timeout = value[1] value = value[0] newvalue = self._temp_set(value, timeout) logger.measure(f"{self.instName}.temp == {newvalue}") self._setpoint = float(value) def _temp_set(self, temp=None, timeout=600): """get/set the setpoint temperature.""" if temp is None: return float(self.inst.query('SETP?')) # elif self._setpoint==None or float(temp) != self._setpoint: if -99.9 <= temp <= 225.0: self.inst.write(f'SETP {temp}') else: fmtstr = f'temp value {temp} must be in [-99.9, 225.0]' logger.error(fmtstr) raise ValueError(fmtstr) # if int(self.inst.query('HEAD?'))==self.Head.up.value: # fmtstr = "{}.temp: Thermo Stream can set Temperature, if head is 'Up'".format(self.instName) # logger.error(fmtstr) # raise ValueError(fmtstr) if int(self.inst.query('FLOW?')) == self.Flow.off.value: self.flow = self.Flow.on logger.measure("{}.temp := {}".format(self.instName, temp)) # wait until steady state of desired temperature start = datetime.now() with spinner(): while 1: lifesign = False self.inst.write('TECR?') # read temperature event condition register if self.inst.read() == '1': break if ((datetime.now() - start).seconds) % 30 == 0: print('\r {} not at temperature or soak time not elapsed {}°C != {}°C, timeout in {}s \r' .format(self.instName, float(self.inst.query('TEMP?')), temp, timeout-(datetime.now() - start).seconds), end='') lifesign = True if timeout > 0 and (datetime.now() - start).seconds > timeout: fmtstr = 'timeout: T_set = {}, T_meas = {}' raise TimeoutError(fmtstr.format(temp, self.temp)) time.sleep(2) if lifesign: print(' ') self._setpoint = float(temp) return self.temp
[docs] def get_state(self): """Get actual state and configuration.""" lines = [] lines.append('Setup File: {}'.format(self.setupfile)) lines.append('lower temp limit: {}'.format(self.llim)) lines.append('upper temp limit: {}'.format(self.ulim)) lines.append('temp window: {}'.format(self.window)) lines.append('soak time: {}s'.format(self.soak)) lines.append('flow rate: {}'.format(self.flowrate)) lines.append('flow: {}'.format(self.flow)) lines.append('head: {}'.format(self.head)) lines.append('temp: {}'.format(self.temp)) lines.append('temp_set: {}'.format(self._temp_set())) _dutmode = { 0: 'off (air control)', 1: 'on (dut control)', 2: 'TC meter mode', } val = self.dut_mode lines.append('dut mode: {}: {}'.format(val, _dutmode[val])) lines.append('dut sensor type: {}'.format(self.dutsensortype)) for i in lines: logger.info(i) return lines
if __name__ == '__main__': from pylab_ml.base_instrument import logsetup logsetup() thermo = MPI_TA5K(addr=1, instName='thermo') # thermo.setupfile='GPIB-Config' thermo.get_state() # thermo.headlock # for Thermostreamer at 3d-Coil thermo.flow = 1 thermo.head = 'up' thermo.head = 'down' thermo.flow = 'on' thermo.flow thermo.flow = 1 thermo.flow = thermo.Flow.on thermo.flow = "sjflskjf" thermo.state thermo.temp = 20, 100 thermo.state thermo.ulim thermo.ulim = 450 thermo.llim thermo.window thermo.soak thermo.ramp thermo.flow thermo.dutsensortype thermo.dutmode print('Temperatur= {}°C'.format(thermo.temp)) thermo.temp = 30, 100 thermo.temp = 20 thermo.flow = 'off' thermo.close()