Source code for apstools.devices.lakeshore_controllers

"""
Lakeshore temperature controllers
+++++++++++++++++++++++++++++++++

.. autosummary::

   ~LakeShore336Device
   ~LakeShore340Device

.. not yet implemented
   ~LakeShore330Device
   ~LakeShore331Device
   ~LakeShore335Device
   ~LakeShoreDR93CADevice
"""

# TODO: refactor
#    temperature = Component(PVPositionerSoftDoneWithStop, ...)

from ophyd import Component
from ophyd import Device
from ophyd import EpicsSignal
from ophyd import EpicsSignalRO
from ophyd import EpicsSignalWithRBV
from ophyd import FormattedComponent
from ophyd import Signal

from . import PVPositionerSoftDoneWithStop
from ..synApps import AsynRecord

SECOND = 1
MINUTE = 60 * SECOND
HOUR = 60 * MINUTE


[docs]class LakeShore336_LoopControl(PVPositionerSoftDoneWithStop): """ LakeShore 336 temperature controller -- with heater control. The LakeShore 336 accepts up to two heaters. """ # position # readback is provided by PVPositionerSoftDoneWithStop setpoint = FormattedComponent( EpicsSignalWithRBV, "{prefix}OUT{loop_number}:SP", put_complete=True ) # configuration units = FormattedComponent( EpicsSignalWithRBV, "{prefix}IN{loop_number}:Units", kind="config" ) heater = FormattedComponent( EpicsSignalRO, "{prefix}HTR{loop_number}", auto_monitor=True, kind="normal" ) heater_range = FormattedComponent( EpicsSignalWithRBV, "{prefix}HTR{loop_number}:Range", kind="config", auto_monitor=True, string=True, ) pid_P = FormattedComponent( EpicsSignalWithRBV, "{prefix}P{loop_number}", kind="config" ) pid_I = FormattedComponent( EpicsSignalWithRBV, "{prefix}I{loop_number}", kind="config" ) pid_D = FormattedComponent( EpicsSignalWithRBV, "{prefix}D{loop_number}", kind="config" ) ramp_rate = FormattedComponent( EpicsSignalWithRBV, "{prefix}RampR{loop_number}", kind="config" ) ramp_on = FormattedComponent( EpicsSignalWithRBV, "{prefix}OnRamp{loop_number}", kind="config" ) loop_name = FormattedComponent( EpicsSignalRO, "{prefix}IN{loop_number}:Name_RBV", kind="config" ) control = FormattedComponent( EpicsSignalWithRBV, "{prefix}OUT{loop_number}:Cntrl", kind="config" ) manual = FormattedComponent( EpicsSignalWithRBV, "{prefix}OUT{loop_number}:MOUT", kind="config" ) mode = FormattedComponent( EpicsSignalWithRBV, "{prefix}OUT{loop_number}:Mode", kind="config" ) def __init__(self, *args, loop_number=None, timeout=10 * HOUR, **kwargs): self.loop_number = loop_number super().__init__( *args, timeout=timeout, tolerance=0.1, readback_pv=f"IN{loop_number}", **kwargs ) self._settle_time = 0 @property def settle_time(self): return self._settle_time @settle_time.setter def settle_time(self, value): if value < 0: raise ValueError("Settle value needs to be >= 0.") else: self._settle_time = value def pause(self): self.setpoint.put(self._position)
[docs]class LakeShore336_LoopRO(Device): """ LakeShore 336 temperature controller -- Read-only loop (no heaters). """ # Set this to normal because we don't use it. readback = FormattedComponent( EpicsSignalRO, "{prefix}IN{loop_number}", kind="normal" ) units = FormattedComponent( EpicsSignalWithRBV, "{prefix}IN{loop_number}:Units", kind="omitted" ) loop_name = FormattedComponent( EpicsSignalRO, "{prefix}IN{loop_number}:Name_RBV", kind="omitted" ) def __init__(self, *args, loop_number=None, **kwargs): self.loop_number = loop_number super().__init__(*args, **kwargs)
[docs]class LakeShore336Device(Device): """ LakeShore 336 temperature controller. - loop 1: temperature positioner AND heater, PID, & ramp controls - loop 2: temperature positioner AND heater, PID, & ramp controls - loop 3: temperature positioner - loop 4: temperature positioner """ loop1 = FormattedComponent( LakeShore336_LoopControl, "{self.prefix}", loop_number=1 ) loop2 = FormattedComponent( LakeShore336_LoopControl, "{self.prefix}", loop_number=2 ) loop3 = FormattedComponent( LakeShore336_LoopRO, "{self.prefix}", loop_number=3 ) loop4 = FormattedComponent( LakeShore336_LoopRO, "{self.prefix}", loop_number=4 ) # same names as apstools.synApps._common.EpicsRecordDeviceCommonAll scanning_rate = Component(EpicsSignal, "read.SCAN", kind="omitted") process_record = Component(EpicsSignal, "read.PROC", kind="omitted") read_all = Component(EpicsSignal, "readAll.PROC", kind="omitted") serial = Component(AsynRecord, "serial", kind="omitted")
[docs]class LS340_LoopBase(PVPositionerSoftDoneWithStop): """ Base settings for both sample and control loops. """ # position # readback is provided by PVPositionerSoftDoneWithStop # TODO: Polar reports: sometimes the setpoint PV is not updated setpoint = FormattedComponent( EpicsSignal, "{prefix}SP{loop_number}", write_pv="{prefix}wr_SP{loop_number}", kind="normal", put_complete=True, ) # configuration units = Component(Signal, value="K", kind="config") pid_P = FormattedComponent( EpicsSignal, "{prefix}P{loop_number}", write_pv="{prefix}setPID{loop_number}.AA", kind="config", ) pid_I = FormattedComponent( EpicsSignal, "{prefix}I{loop_number}", write_pv="{prefix}setPID{loop_number}.BB", kind="config", ) pid_D = FormattedComponent( EpicsSignal, "{prefix}D{loop_number}", write_pv="{prefix}setPID{loop_number}.CC", kind="config", ) ramp_rate = FormattedComponent( EpicsSignal, "{prefix}Ramp{loop_number}", write_pv="{prefix}setRamp{loop_number}.BB", kind="config", ) ramp_on = FormattedComponent( EpicsSignal, "{prefix}Ramp{loop_number}_on", kind="config" ) def __init__(self, *args, loop_number=None, timeout=10 * HOUR, **kwargs): self.loop_number = loop_number super().__init__( *args, readback_pv="ignore", timeout=timeout, tolerance=0.1, **kwargs ) self._settle_time = 0 @property def settle_time(self): return self._settle_time @settle_time.setter def settle_time(self, value): if value < 0: raise ValueError("Settle value needs to be >= 0.") else: self._settle_time = value @property def egu(self): return self.units.get(as_string=True) def pause(self): self.setpoint.put(self._position, wait=True)
[docs]class LS340_LoopControl(LS340_LoopBase): """ Control specific """ readback = Component(EpicsSignalRO, "Control", kind="normal") sensor = Component(EpicsSignal, "Ctl_sel", kind="config")
[docs]class LS340_LoopSample(LS340_LoopBase): """ Sample specific """ readback = Component(EpicsSignalRO, "Sample", kind="hinted") sensor = Component(EpicsSignal, "Spl_sel", kind="config")
[docs]class LakeShore340Device(Device): """ LakeShore 340 temperature controller """ control = FormattedComponent(LS340_LoopControl, "{prefix}", loop_number=1) sample = FormattedComponent(LS340_LoopSample, "{prefix}", loop_number=2) heater = Component(EpicsSignalRO, "Heater") heater_range = Component( EpicsSignal, "Rg_rdbk", write_pv="HeatRg", kind="normal", put_complete=True ) read_pid = Component(EpicsSignal, "readPID.PROC", kind="omitted") # same names as apstools.synApps._common.EpicsRecordDeviceCommonAll scanning_rate = Component(EpicsSignal, "read.SCAN", kind="omitted") process_record = Component(EpicsSignal, "read.PROC", kind="omitted") serial = Component(AsynRecord, "serial", kind="omitted")