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 ..synApps import AsynRecord
from ..utils import HOUR
from . import PVPositionerSoftDoneWithStop


[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, use_target=True, 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
[docs] def pause(self): """Change setpoint to current position.""" 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, use_target=True, 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)
[docs] def pause(self): """Change setpoint to current position.""" 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")
# ----------------------------------------------------------------------------- # :author: Pete R. Jemian # :email: jemian@anl.gov # :copyright: (c) 2017-2024, UChicago Argonne, LLC # # Distributed under the terms of the Argonne National Laboratory Open Source License. # # The full license is in the file LICENSE.txt, distributed with this software. # -----------------------------------------------------------------------------