Source code for py4syn.epics.LinkamCI94Class

"""Linkam CI94 temperature controller class

Python class for Linkam CI94 temperature controllers

:platform: Unix
:synopsis: Python class for Linkam CI94 temperature controllers

.. moduleauthor:: Henrique Dante de Almeida <henrique.almeida@lnls.br>

"""
from threading import Event

from epics import Device, ca

from py4syn.epics.IScannable import IScannable
from py4syn.epics.StandardDevice import StandardDevice
from py4syn.utils.timer import Timer

[docs]class LinkamCI94(StandardDevice, IScannable): """ Class to control Linkam CI94 temperature controllers via EPICS. Examples -------- >>> from py4syn.epics.LinkamCI94Class import LinkamCI94 >>> >>> def showTemperature(pv='', name=''): ... ci94 = LinkamCI94(pv, name) ... print('Temperature is: %d' % ci94.getValue()) ... >>> def fastRaiseTemperature(ci94, amount, rate=30): ... ci94.setRate(rate) ... ci94.setValue(ci94.getValue() + amount) ... >>> def complexRamp(ci94): ... ci94.setPumpSpeed(-1) ... ci94.setRate(10) ... ci94.setValue(200) ... ci94.wait() ... ci94.setRate(2) ... ci94.setValue(220) ... ci94.wait() ... sleep(500) ... ci94.setRate(5) ... ci94.setValue(100) ... ci94.wait() ... ci94.stop() ... >>> import py4syn >>> from py4syn.epics.ScalerClass import Scaler >>> from py4syn.utils.counter import createCounter >>> from py4syn.utils.scan import scan >>> >>> def temperatureScan(start, end, rate, pv='', counter='', channel=2): ... ci94 = LinkamCI94(pv, 'ci94') ... py4syn.mtrDB['ci94'] = ci94 ... c = Scaler(counter, channel, 'simcountable') ... createCounter('counter', c, channel) ... ci94.setRate(rate) ... scan('ci94', start, end, 10, 1) ... ci94.stop() ... """ STATUS_STOPPED = 0 PUMP_AUTOMATIC = 0 PUMP_MANUAL = 1 def __init__(self, pvName, mnemonic): """ **Constructor** See :class:`py4syn.epics.StandardDevice` Parameters ---------- pvName : `string` Power supply base naming of the PV (Process Variable) mnemonic : `string` Temperature controller mnemonic """ super().__init__(mnemonic) self.pvName = pvName self.linkam = Device(pvName + ':', ['setRate', 'setLimit', 'pending', 'temp', 'stop', 'setSpeed', 'pumpMode', 'status', 'start']) self.done = Event() self.newTemperature = Event() self.pending = bool(self.linkam.get('pending')) self.setRate(5) self.linkam.add_callback('pending', self.onPendingChange) self.linkam.add_callback('temp', self.onTemperatureChange) def __str__(self): return '%s (%s)' % (self.getMnemonic(), self.pvName)
[docs] def isRunning(self): """ Returns true if the controller is in program mode. Whenever it is program mode, it is following a target temperature. Returns ------- `bool` """ v = self.linkam.get('status') return v != self.STATUS_STOPPED
[docs] def getValue(self): """ Returns the current measured temperature. Returns ------- `float` """ return self.linkam.get('temp')
[docs] def getRealPosition(self): """ Returns the same as :meth:`getValue`. See: :meth:`getValue` Returns ------- `float` """ return self.getValue()
[docs] def onPendingChange(self, value, **kwargs): """ Helper callback that tracks when the IOC finished changing to a requested temperature. """ self.pending = bool(value) if not self.pending: self.done.set()
[docs] def onTemperatureChange(self, **kwargs): """ Helper callback that indicates when the measured temperature changed. """ self.newTemperature.set()
[docs] def setRate(self, r): """ Sets the ramp speed in degrees per minutes for use with :meth:`setValue`. See: :meth:`setValue` Parameters ---------- r : `float` Ramp speed in °C/min """ self.linkam.put('setRate', r)
[docs] def setVelocity(self, velo): """ Same as :meth:`setRate`. See: :meth:`setRate` Parameters ---------- r : `float` Ramp speed in °C/min """ self.setRate(velo)
[docs] def setValue(self, v): """ Changes the temperature to a new value. The speed that the new temperature is reached is set with :meth:`setRate`. The default rate is 5 °C/minute. See: :meth:`setRate` Parameters ---------- v : `float` The target temperature in °C """ self.done.clear() self.pending = True self.requestedValue = v self.linkam.put('setLimit', v) if not self.isRunning(): self.run()
[docs] def wait(self): """ Blocks until the requested temperature is achieved. """ if not self.pending: return # Waiting is done in two steps. First step waits until the IOC deasserts # the pending flag to indicate a complete operation. Second step waits util the # measured temperature reaches the requested temperature. ca.flush_io() self.done.wait() self.newTemperature.clear() timeout = Timer(7) while self.getValue() != self.requestedValue and timeout.check(): self.newTemperature.wait(1) self.newTemperature.clear()
[docs] def getLowLimitValue(self): """ Returns the controller low limit temperature. Returns ------- `float` """ return -196
[docs] def getHighLimitValue(self): """ Returns the controller high limit temperature. Returns ------- `float` """ return 1500
[docs] def run(self): """ Starts or resumes executing the current temperature program. """ self.linkam.put('start', 1)
[docs] def stop(self): """ Stops executing the current temperature program, stops the nitrogen pump and puts the device in idle state. In the idle state, the device will not try to set a target temperature. """ self.setPumpSpeed(0) self.linkam.put('stop', 1)
[docs] def setPumpSpeed(self, speed): """ Changes the nitrogen pump speed, or enables automatic pump speed control. .. note:: The Linkam front panel only has 5 LEDs to indicate speed, but internally it supports 30 different speed levels. Parameters ---------- speed : `int` The requested pump speed, ranging from 0 (pump off) to 30 (pump top speed), or -1 to enable automatic pump control. """ if speed < -1 or speed > 30: raise ValueError('Invalid speed') if speed == -1: self.linkam.put('pumpMode', self.PUMP_AUTOMATIC) return self.linkam.put('pumpMode', self.PUMP_MANUAL, wait=True) self.linkam.put('setSpeed', speed)