Added devices and updated config

This commit is contained in:
gac-x01da
2025-03-11 09:41:40 +01:00
committed by appel_c
parent 87ab69b335
commit 7f07f4a3dd
7 changed files with 937 additions and 10 deletions

View File

@@ -360,15 +360,15 @@ fm_ztcp:
onFailure: retry
enabled: true
softwareTrigger: false
fm_xstripe:
readoutPriority: baseline
description: Focusing Morror X Stripe
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: X01DA-OP-FM:XSTRIPE
onFailure: retry
enabled: true
softwareTrigger: false
# fm_xstripe:
# readoutPriority: baseline
# description: Focusing Morror X Stripe
# deviceClass: ophyd.EpicsMotor
# deviceConfig:
# prefix: X01DA-OP-FM:XSTRIPE
# onFailure: retry
# enabled: true
# softwareTrigger: false
## Optics Slits 1 -- Physical positioners
@@ -782,7 +782,7 @@ es1arc_roty:
description: End Station segmented arc Y-rotation
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: X01DA-ES1-MAN2:ROTY
prefix: X01DA-ES1-ARC:ROTY
onFailure: retry
enabled: true
softwareTrigger: false

View File

@@ -17,6 +17,8 @@ dummy_pv:
onFailure: retry
enabled: true
softwareTrigger: false
## NIDAQ
nidaq:
readoutPriority: async
description: NIDAQ backend for data reading for debye scans
@@ -27,3 +29,171 @@ nidaq:
enabled: true
softwareTrigger: false
# ES0 Filter
# es0filter:
# readoutPriority: async
# description: ES0 filter station
# deviceClass: debye_bec.devices.es0filter.ES0Filter
# deviceConfig:
# prefix: "X01DA-ES0-FI:"
# onFailure: retry
# enabled: true
# softwareTrigger: false
# Current amplifiers
# amplifiers:
# readoutPriority: async
# description: ES current amplifiers
# deviceClass: debye_bec.devices.amplifiers.Amplifiers
# deviceConfig:
# prefix: "X01DA-ES:AMP5004"
# onFailure: retry
# enabled: true
# softwareTrigger: false
# HV power supplies
# hv_supplies:
# readoutPriority: async
# description: HV power supplies
# deviceClass: debye_bec.devices.hv_supplies.HVSupplies
# deviceConfig:
# prefix: "X01DA-"
# onFailure: retry
# enabled: true
# softwareTrigger: false
# Gas Mix Setup
# gas_mix_setup:
# readoutPriority: async
# description: Gas Mix Setup for Ionization Chambers
# deviceClass: debye_bec.devices.gas_mix_setup.GasMixSetup
# deviceConfig:
# prefix: "X01DA-ES-GMES:"
# onFailure: retry
# enabled: true
# softwareTrigger: false
# Pilatus Curtain
# pilatus_curtain:
# readoutPriority: async
# description: Pilatus Curtain
# deviceClass: debye_bec.devices.pilatus_curtain.PilatusCurtain
# deviceConfig:
# prefix: "X01DA-ES2-DET3:TRY-"
# onFailure: retry
# enabled: true
# softwareTrigger: false
################################
## ES Hutch Sensors and Light ##
################################
es_temperature1:
readoutPriority: monitored
description: ES temperature sensor 1
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: "X01DA-PC-I2C:_CH1:TEMP"
onFailure: retry
enabled: true
softwareTrigger: false
es_humidity1:
readoutPriority: monitored
description: ES humidity sensor 1
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: "X01DA-PC-I2C:_CH1:HUMIREL"
onFailure: retry
enabled: true
softwareTrigger: false
es_pressure1:
readoutPriority: monitored
description: ES ambient pressure sensor 1
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: "X01DA-PC-I2C:_CH1:PRES"
onFailure: retry
enabled: true
softwareTrigger: false
es_temperature2:
readoutPriority: monitored
description: ES temperature sensor 2
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: "X01DA-PC-I2C:_CH2:TEMP"
onFailure: retry
enabled: true
softwareTrigger: false
es_humidity2:
readoutPriority: monitored
description: ES humidity sensor 2
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: "X01DA-PC-I2C:_CH2:HUMIREL"
onFailure: retry
enabled: true
softwareTrigger: false
es_pressure2:
readoutPriority: monitored
description: ES ambient pressure sensor 2
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: "X01DA-PC-I2C:_CH2:PRES"
onFailure: retry
enabled: true
softwareTrigger: false
es_light_toggle:
readoutPriority: monitored
description: ES light toggle
deviceClass: ophyd.EpicsSignal
deviceConfig:
read_pv: "X01DA-EH-LIGHT:TOGGLE"
onFailure: retry
enabled: true
softwareTrigger: false
#################
## SDD sensors ##
#################
sdd1_temperature:
readoutPriority: monitored
description: SDD1 temperature sensor
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: "X01DA-ES1-DET1:Temperature"
onFailure: retry
enabled: true
softwareTrigger: false
sdd1_humidity:
readoutPriority: monitored
description: SDD1 humidity sensor
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: "X01DA-ES1-DET1:Humidity"
onFailure: retry
enabled: true
softwareTrigger: false
#####################
## Alignment Laser ##
#####################
es1_alignment_laser:
readoutPriority: monitored
description: ES1 alignment laser
deviceClass: ophyd.EpicsSignal
deviceConfig:
read_pv: "X01DA-ES1-LAS:Relay"
onFailure: retry
enabled: true
softwareTrigger: false

View File

@@ -0,0 +1,186 @@
""" ES Current amplifiers"""
import enum
from typing import Literal
from ophyd import Component as Cpt
from ophyd import Device, Kind, EpicsSignalWithRBV
from ophyd_devices.utils import bec_utils
class Enable(int, enum.Enum):
"""Enum class for the enable signal of the channel"""
OFF = 0
STARTUP = 1
ON = 2
class Gain(int, enum.Enum):
"""Enum class for the gain of the channel"""
G1E6 = 0
G1E7 = 1
G5E7 = 2
G1E8 = 3
G1E9 = 4
class Filter(int, enum.Enum):
"""Enum class for the filter of the channel"""
F1US = 0
F3US = 1
F10US = 2
F30US = 3
F100US = 4
F300US = 5
F1MS = 6
F3MS = 7
class Amplifiers(Device):
"""Class for the ES current amplifiers"""
USER_ACCESS = ['set_channel']
ic0_enable = Cpt(
EpicsSignalWithRBV, suffix=".cOnOff1", kind="config", doc='Enable ch1 -> IC0'
)
ic0_gain = Cpt(
EpicsSignalWithRBV, suffix=":cGain1_ENUM", kind="config", doc='Gain of ch1 -> IC0'
)
ic0_filter = Cpt(
EpicsSignalWithRBV, suffix=":cFilter1_ENUM", kind="config", doc='Filter of ch1 -> IC0'
)
ic1_enable = Cpt(
EpicsSignalWithRBV, suffix=".cOnOff2", kind="config", doc='Enable ch2 -> IC1'
)
ic1_gain = Cpt(
EpicsSignalWithRBV, suffix=":cGain2_ENUM", kind="config", doc='Gain of ch2 -> IC1'
)
ic1_filter = Cpt(
EpicsSignalWithRBV, suffix=":cFilter2_ENUM", kind="config", doc='Filter of ch2 -> IC1'
)
ic2_enable = Cpt(
EpicsSignalWithRBV, suffix=".cOnOff3", kind="config", doc='Enable ch3 -> IC2'
)
ic2_gain = Cpt(
EpicsSignalWithRBV, suffix=":cGain3_ENUM", kind="config", doc='Gain of ch3 -> IC2'
)
ic2_filter = Cpt(
EpicsSignalWithRBV, suffix=":cFilter3_ENUM", kind="config", doc='Filter of ch3 -> IC2'
)
pips_enable = Cpt(
EpicsSignalWithRBV, suffix=".cOnOff4", kind="config", doc='Enable ch4 -> PIPS'
)
pips_gain = Cpt(
EpicsSignalWithRBV, suffix=":cGain4_ENUM", kind="config", doc='Gain of ch4 -> PIPS'
)
pips_filter = Cpt(
EpicsSignalWithRBV, suffix=":cFilter4_ENUM", kind="config", doc='Filter of ch4 -> PIPS'
)
def __init__(
self, prefix="", *, name: str, kind: Kind = None, device_manager=None, parent=None, **kwargs
):
"""Initialize the Current Amplifiers.
Args:
prefix (str): EPICS prefix for the device
name (str): Name of the device
kind (Kind): Kind of the device
device_manager (DeviceManager): Device manager instance
parent (Device): Parent device
kwargs: Additional keyword arguments
"""
super().__init__(prefix, name=name, kind=kind, parent=parent, **kwargs)
self.device_manager = device_manager
self.service_cfg = None
self.timeout_for_pvwait = 2.5
self.readback.name = self.name
# Wait for connection on all components, ensure IOC is connected
self.wait_for_connection(all_signals=True, timeout=5)
if device_manager:
self.device_manager = device_manager
else:
self.device_manager = bec_utils.DMMock()
self.connector = self.device_manager.connector
def set_channel(
self,
detector: Literal['ic0', 'ic1', 'ic2', 'pips'],
gain: Literal['1e6', '1e7', '5e7', '1e8', '1e9'],
filter: Literal['1us', '3us', '10us', '30us', '100us', '300us', '1ms', '3ms']
) -> None:
"""Configure the gain setting of the specified channel
Args:
detector (Literal['ic0', 'ic1', 'ic2', 'pips']) : Detector
gain (Literal['1e6', '1e7', '5e7', '1e8', '1e9']) : Desired gain
filter (Literal['1us', '3us', '10us', '30us', '100us', '300us', '1ms', '3ms']) : Desired filter
"""
ch_enable = None
ch_gain = None
ch_filter = None
match detector:
case 'ic0':
ch_enable = self.ic0_enable
ch_gain = self.ic0_gain
ch_filter = self.ic0_filter
case 'ic1':
ch_enable = self.ic1_enable
ch_gain = self.ic1_gain
ch_filter = self.ic1_filter
case 'ic2':
ch_enable = self.ic2_enable
ch_gain = self.ic2_gain
ch_filter = self.ic2_filter
case 'pips':
ch_enable = self.pips_enable
ch_gain = self.pips_gain
ch_filter = self.pips_filter
ch_enable.put(Enable.ON)
# Wait until channel is switched on
if not self.wait_for_signals(
signal_conditions=[(ch_enable.get, Enable.ON)],
timeout=self.timeout_for_pvwait,
check_stopped=True,
):
raise TimeoutError(
f"Enabling channel run into timeout after {self.timeout_for_pvwait} seconds"
)
match gain:
case '1e6':
ch_gain.put(Gain.G1E6)
case '1e7':
ch_gain.put(Gain.G1E7)
case '5e7':
ch_gain.put(Gain.G5E7)
case '1e8':
ch_gain.put(Gain.G1E8)
case '1e9':
ch_gain.put(Gain.G1E9)
match filter:
case '1us':
ch_filter.put(Filter.F1US)
case '3us':
ch_filter.put(Filter.F3US)
case '10us':
ch_filter.put(Filter.F10US)
case '30us':
ch_filter.put(Filter.F30US)
case '100us':
ch_filter.put(Filter.F100US)
case '300us':
ch_filter.put(Filter.F300US)
case '1ms':
ch_filter.put(Filter.F1MS)
case '3ms':
ch_filter.put(Filter.F3MS)

View File

@@ -0,0 +1,89 @@
""" ES0 Filter Station"""
from ophyd import Component as Cpt
from ophyd import Device, Kind, EpicsSignalWithRBV
from ophyd_devices.utils import bec_utils
class ES0Filter(Device):
"""Class for the ES0 filter station"""
USER_ACCESS = ['set_filters']
filter_output = Cpt(
EpicsSignalWithRBV,
suffix="BIO",
kind="config",
doc='Packed value of filter positions'
)
def __init__(
self, prefix="", *, name: str, kind: Kind = None, device_manager=None, parent=None, **kwargs
):
"""Initialize the ES0 Filter Station.
Args:
prefix (str): EPICS prefix for the device
name (str): Name of the device
kind (Kind): Kind of the device
device_manager (DeviceManager): Device manager instance
parent (Device): Parent device
kwargs: Additional keyword arguments
"""
super().__init__(prefix, name=name, kind=kind, parent=parent, **kwargs)
self.device_manager = device_manager
self.service_cfg = None
self.timeout_for_pvwait = 2.5
self.readback.name = self.name
# Wait for connection on all components, ensure IOC is connected
self.wait_for_connection(all_signals=True, timeout=5)
if device_manager:
self.device_manager = device_manager
else:
self.device_manager = bec_utils.DMMock()
self.connector = self.device_manager.connector
def set_filters(self, filters: list) -> None:
"""Configure the filters according to the list
Args:
filters (list) : List of strings representing the filters, e.g. ['Mo400', 'Al20']
"""
output = 0
for filter in filters:
match filter:
case 'Mo400':
output = output & (1 << 1)
case 'Mo300':
output = output & (1 << 2)
case 'Mo200':
output = output & (1 << 3)
case 'Zn500':
output = output & (1 << 4)
case 'Zn250':
output = output & (1 << 5)
case 'Zn125':
output = output & (1 << 6)
case 'Zn50':
output = output & (1 << 7)
case 'Zn25':
output = output & (1 << 8)
case 'Al500':
output = output & (1 << 9)
case 'Al320':
output = output & (1 << 10)
case 'Al200':
output = output & (1 << 11)
case 'Al100':
output = output & (1 << 12)
case 'Al50':
output = output & (1 << 13)
case 'Al20':
output = output & (1 << 14)
case 'Al10':
output = output & (1 << 15)
self.filter_output.put(output)

View File

@@ -0,0 +1,236 @@
""" ES Gas Mix Setup"""
import time
from typing import Literal
from ophyd import Component as Cpt
from ophyd import Device, Kind, EpicsSignalWithRBV, EpicsSignal, EpicsSignalRO
from ophyd_devices.utils import bec_utils
class GasMixSetup(Device):
"""Class for the ES HGas Mix Setup"""
USER_ACCESS = ['fill_ic']
# IC0
ic0_gas1_req = Cpt(
EpicsSignalWithRBV, suffix="IC0Gas1Req", kind="config", doc='IC0 Gas 1 requirement'
)
ic0_conc1_req = Cpt(
EpicsSignalWithRBV, suffix="IC0Conc1Req", kind="config", doc='IC0 Concentration 1 requirement'
)
ic0_gas2_req = Cpt(
EpicsSignalWithRBV, suffix="IC0Gas2Req", kind="config", doc='IC0 Gas 2 requirement'
)
ic0_conc2_req = Cpt(
EpicsSignalWithRBV, suffix="IC0Conc2Req", kind="config", doc='IC0 Concentration 2 requirement'
)
ic0_press_req = Cpt(
EpicsSignalWithRBV, suffix="IC0PressReq", kind="config", doc='IC0 Pressure requirement'
)
ic0_fill = Cpt(
EpicsSignal, suffix="IC0Fill", kind="config", doc='Fill IC0'
)
ic0_status = Cpt(
EpicsSignalRO, suffix="IC0Status", kind="config", doc='Status of IC0'
)
ic0_gas1 = Cpt(
EpicsSignalRO, suffix="IC0Gas1", kind="config", doc='IC0 Gas 1'
)
ic0_conc1 = Cpt(
EpicsSignalRO, suffix="IC0Conc1", kind="config", doc='IC0 Concentration 1'
)
ic0_gas2 = Cpt(
EpicsSignalRO, suffix="IC0Gas2", kind="config", doc='IC0 Gas 2'
)
ic0_conc2 = Cpt(
EpicsSignalRO, suffix="IC0Conc2", kind="config", doc='IC0 Concentration 2'
)
ic0_press = Cpt(
EpicsSignalRO, suffix="IC0PressTransm", kind="config", doc='Current IC0 Pressure'
)
# IC1
ic1_gas1_req = Cpt(
EpicsSignalWithRBV, suffix="IC1Gas1Req", kind="config", doc='IC1 Gas 1 requirement'
)
ic1_conc1_req = Cpt(
EpicsSignalWithRBV, suffix="IC1Conc1Req", kind="config", doc='IC1 Concentration 1 requirement'
)
ic1_gas2_req = Cpt(
EpicsSignalWithRBV, suffix="IC1Gas2Req", kind="config", doc='IC1 Gas 2 requirement'
)
ic1_conc2_req = Cpt(
EpicsSignalWithRBV, suffix="IC1Conc2Req", kind="config", doc='IC1 Concentration 2 requirement'
)
ic1_press_req = Cpt(
EpicsSignalWithRBV, suffix="IC1PressReq", kind="config", doc='IC1 Pressure requirement'
)
ic1_fill = Cpt(
EpicsSignal, suffix="IC1Fill", kind="config", doc='Fill IC1'
)
ic1_status = Cpt(
EpicsSignalRO, suffix="IC1Status", kind="config", doc='Status of IC1'
)
ic1_gas1 = Cpt(
EpicsSignalRO, suffix="IC1Gas1", kind="config", doc='IC1 Gas 1'
)
ic1_conc1 = Cpt(
EpicsSignalRO, suffix="IC1Conc1", kind="config", doc='IC1 Concentration 1'
)
ic1_gas2 = Cpt(
EpicsSignalRO, suffix="IC1Gas2", kind="config", doc='IC1 Gas 2'
)
ic1_conc2 = Cpt(
EpicsSignalRO, suffix="IC1Conc2", kind="config", doc='IC1 Concentration 2'
)
ic1_press = Cpt(
EpicsSignalRO, suffix="IC1PressTransm", kind="config", doc='Current IC1 Pressure'
)
# IC2
ic2_gas1_req = Cpt(
EpicsSignalWithRBV, suffix="IC2Gas1Req", kind="config", doc='IC2 Gas 1 requirement'
)
ic2_conc1_req = Cpt(
EpicsSignalWithRBV, suffix="IC2Conc1Req", kind="config", doc='IC2 Concentration 1 requirement'
)
ic2_gas2_req = Cpt(
EpicsSignalWithRBV, suffix="IC2Gas2Req", kind="config", doc='IC2 Gas 2 requirement'
)
ic2_conc2_req = Cpt(
EpicsSignalWithRBV, suffix="IC2Conc2Req", kind="config", doc='IC2 Concentration 2 requirement'
)
ic2_press_req = Cpt(
EpicsSignalWithRBV, suffix="IC2PressReq", kind="config", doc='IC2 Pressure requirement'
)
ic2_fill = Cpt(
EpicsSignal, suffix="IC2Fill", kind="config", doc='Fill IC2'
)
ic2_status = Cpt(
EpicsSignalRO, suffix="IC2Status", kind="config", doc='Status of IC2'
)
ic2_gas1 = Cpt(
EpicsSignalRO, suffix="IC2Gas1", kind="config", doc='IC2 Gas 1'
)
ic2_conc1 = Cpt(
EpicsSignalRO, suffix="IC2Conc1", kind="config", doc='IC2 Concentration 1'
)
ic2_gas2 = Cpt(
EpicsSignalRO, suffix="IC2Gas2", kind="config", doc='IC2 Gas 2'
)
ic2_conc2 = Cpt(
EpicsSignalRO, suffix="IC2Conc2", kind="config", doc='IC2 Concentration 2'
)
ic2_press = Cpt(
EpicsSignalRO, suffix="IC2PressTransm", kind="config", doc='Current IC2 Pressure'
)
def __init__(
self, prefix="", *, name: str, kind: Kind = None, device_manager=None, parent=None, **kwargs
):
"""Initialize the Gas Mix Setup.
Args:
prefix (str): EPICS prefix for the device
name (str): Name of the device
kind (Kind): Kind of the device
device_manager (DeviceManager): Device manager instance
parent (Device): Parent device
kwargs: Additional keyword arguments
"""
super().__init__(prefix, name=name, kind=kind, parent=parent, **kwargs)
self.device_manager = device_manager
self.service_cfg = None
self.timeout_for_pvwait = 360
self.readback.name = self.name
# Wait for connection on all components, ensure IOC is connected
self.wait_for_connection(all_signals=True, timeout=5)
if device_manager:
self.device_manager = device_manager
else:
self.device_manager = bec_utils.DMMock()
self.connector = self.device_manager.connector
def fill_ic(
self,
detector: Literal['ic0', 'ic1', 'ic2'],
gas1: Literal['He', 'LN2', 'Ar', 'Kr'],
conc1: float,
gas2: Literal['He', 'LN2', 'Ar', 'Kr'],
conc2: float,
pressure: float,
) -> None:
"""Fill an ionization chamber with the specified gas mixture.
Args:
detector (Literal['ic0', 'ic1', 'ic2']) : Detector
gas1 (Literal['He', 'LN2', 'Ar', 'Kr']) : Gas 1 requirement,
conc1 (float) : Concentration 1 requirement in %,
gas2 (Literal['He', 'LN2', 'Ar', 'Kr']) : Gas 2 requirement,
conc2 (float) : Concentration 2 requirement in %,
pressure (float) : Required pressure in bar abs,
"""
if 100 < conc1 < 0:
raise ValueError('Concentration 1 out of range [0 .. 100 %]')
if 100 < conc2 < 0:
raise ValueError('Concentration 2 out of range [0 .. 100 %]')
if 3 < pressure < 0:
raise ValueError('Pressure out of range [0 .. 3 bar abs]')
ch_gas1_req = None
ch_conc1_req = None
ch_gas2_req = None
ch_conc2_req = None
ch_press_req = None
ch_fill = None
ch_status = None
match detector:
case 'ic0':
ch_gas1_req = self.ic0_gas1_req
ch_conc1_req = self.ic0_conc1_req
ch_gas2_req = self.ic0_gas2_req
ch_conc2_req = self.ic0_conc2_req
ch_press_req = self.ic0_press_req
ch_fill = self.ic0_fill
ch_status = self.ic0_status
case 'ic1':
ch_gas1_req = self.ic1_gas1_req
ch_conc1_req = self.ic1_conc1_req
ch_gas2_req = self.ic1_gas2_req
ch_conc2_req = self.ic1_conc2_req
ch_press_req = self.ic1_press_req
ch_fill = self.ic1_fill
ch_status = self.ic1_status
case 'ic2':
ch_gas1_req = self.ic2_gas1_req
ch_conc1_req = self.ic2_conc1_req
ch_gas2_req = self.ic2_gas2_req
ch_conc2_req = self.ic2_conc2_req
ch_press_req = self.ic2_press_req
ch_fill = self.ic2_fill
ch_status = self.ic2_status
ch_gas1_req.put(gas1)
ch_conc1_req.put(conc1)
ch_gas2_req.put(gas2)
ch_conc2_req.put(conc2)
ch_press_req.put(pressure)
time.sleep(0.5)
ch_fill.put(1)
time.sleep(1)
# Wait until ionization chamber is filled successfully
if not self.wait_for_signals(
signal_conditions=[(ch_status.get, 1)],
timeout=self.timeout_for_pvwait,
check_stopped=True,
):
raise TimeoutError(
f"Ionization chamber still not filled after {self.timeout_for_pvwait} seconds, check caqtdm panel"
)

View File

@@ -0,0 +1,165 @@
""" ES HV power supplies"""
from typing import Literal
from ophyd import Component as Cpt
from ophyd import Device, Kind, EpicsSignalWithRBV, EpicsSignal, EpicsSignalRO
from ophyd_devices.utils import bec_utils
class Amplifiers(Device):
"""Class for the ES HV power supplies"""
USER_ACCESS = ['set_ic']
# IC0
ic0_ext_ena = Cpt(
EpicsSignalRO, suffix="ES1-IC0:HV-Ext-Ena", kind="config", doc='External enable signal of HV IC0'
)
ic0_ena = Cpt(
EpicsSignalWithRBV, suffix="ES1-IC0:HV-Ena", kind="config", doc='Enable signal of HV IC0'
)
ic0_hv_v = Cpt(
EpicsSignal, suffix="ES1-IC0:HV1-VSet", kind="config", doc='HV voltage of IC0'
)
ic0_hv_i = Cpt(
EpicsSignal, suffix="ES1-IC0:HV1-V-RB", kind="config", doc='HV current of IC0'
)
ic0_grid_v = Cpt(
EpicsSignal, suffix="ES1-IC0:HV2-VSet", kind="config", doc='Grid voltage of IC0'
)
ic0_grid_i = Cpt(
EpicsSignal, suffix="ES1-IC0:HV2-V-RB", kind="config", doc='Grid current of IC0'
)
# IC1
ic1_ext_ena = Cpt(
EpicsSignalRO, suffix="ES2-IC12:HV-Ext-Ena", kind="config", doc='External enable signal of HV IC1/IC2'
)
ic1_ena = Cpt(
EpicsSignalWithRBV, suffix="ES2-IC12:HV-Ena", kind="config", doc='Enable signal of HV IC1/IC2'
)
ic1_hv_v = Cpt(
EpicsSignal, suffix="ES2-IC1:HV1-VSet", kind="config", doc='HV voltage of IC1'
)
ic1_hv_i = Cpt(
EpicsSignal, suffix="ES2-IC1:HV1-V-RB", kind="config", doc='HV current of IC1'
)
ic1_grid_v = Cpt(
EpicsSignal, suffix="ES2-IC1:HV2-VSet", kind="config", doc='Grid voltage of IC1'
)
ic1_grid_i = Cpt(
EpicsSignal, suffix="ES2-IC1:HV2-V-RB", kind="config", doc='Grid current of IC1'
)
# IC2
ic2_ext_ena = Cpt(
EpicsSignalRO, suffix="ES2-IC12:HV-Ext-Ena", kind="config", doc='External enable signal of HV IC1/IC2'
)
ic2_ena = Cpt(
EpicsSignalWithRBV, suffix="ES2-IC12:HV-Ena", kind="config", doc='Enable signal of HV IC1/IC2'
)
ic2_hv_v = Cpt(
EpicsSignal, suffix="ES2-IC2:HV1-VSet", kind="config", doc='HV voltage of IC2'
)
ic2_hv_i = Cpt(
EpicsSignal, suffix="ES2-IC2:HV1-V-RB", kind="config", doc='HV current of IC2'
)
ic2_grid_v = Cpt(
EpicsSignal, suffix="ES2-IC2:HV2-VSet", kind="config", doc='Grid voltage of IC2'
)
ic2_grid_i = Cpt(
EpicsSignal, suffix="ES2-IC2:HV2-V-RB", kind="config", doc='Grid current of IC2'
)
def __init__(
self, prefix="", *, name: str, kind: Kind = None, device_manager=None, parent=None, **kwargs
):
"""Initialize the Current Amplifiers.
Args:
prefix (str): EPICS prefix for the device
name (str): Name of the device
kind (Kind): Kind of the device
device_manager (DeviceManager): Device manager instance
parent (Device): Parent device
kwargs: Additional keyword arguments
"""
super().__init__(prefix, name=name, kind=kind, parent=parent, **kwargs)
self.device_manager = device_manager
self.service_cfg = None
self.timeout_for_pvwait = 2.5
self.readback.name = self.name
# Wait for connection on all components, ensure IOC is connected
self.wait_for_connection(all_signals=True, timeout=5)
if device_manager:
self.device_manager = device_manager
else:
self.device_manager = bec_utils.DMMock()
self.connector = self.device_manager.connector
def set_voltage(
self,
detector: Literal['ic0', 'ic1', 'ic2'],
hv: float,
grid: float
) -> None:
"""Configure the voltage settings of the specified detector, this will
enable the high voltage (if external enable is active)!
Args:
detector (Literal['ic0', 'ic1', 'ic2']) : Detector
hv (float) : Desired voltage for the 'HV' terminal
grid (float) : Desired voltage for the 'Grid' terminal
"""
if 3000 < hv < 0:
raise ValueError('specified HV not within range [0 .. 3000]')
if 3000 < grid < 0:
raise ValueError('specified Grid not within range [0 .. 3000]')
if grid > hv:
raise ValueError('Grid must not be higher than HV!')
ch_ena = None
ch_hv_v = None
ch_hv_i = None
ch_grid_v = None
ch_grid_i = None
match detector:
case 'ic0':
ch_ena = self.ic0_ena
ch_hv_v = self.ic0_hv_v
ch_hv_i = self.ic0_hv_i
ch_grid_v = self.ic0_grid_v
ch_grid_i = self.ic0_grid_i
case 'ic1':
ch_ena = self.ic1_ena
ch_hv_v = self.ic1_hv_v
ch_hv_i = self.ic1_hv_i
ch_grid_v = self.ic1_grid_v
ch_grid_i = self.ic1_grid_i
case 'ic2':
ch_ena = self.ic2_ena
ch_hv_v = self.ic2_hv_v
ch_hv_i = self.ic2_hv_i
ch_grid_v = self.ic2_grid_v
ch_grid_i = self.ic2_grid_i
ch_ena.put(1)
# Wait until channel is switched on
if not self.wait_for_signals(
signal_conditions=[(ch_ena.get, 1)],
timeout=self.timeout_for_pvwait,
check_stopped=True,
):
raise TimeoutError(
f"Enabling channel run into timeout after {self.timeout_for_pvwait} seconds"
)
# Set current fixed to 3 mA (max)
ch_hv_i.put(3)
ch_hv_v.put(hv)
ch_grid_i.put(3)
ch_grid_v.put(grid)

View File

@@ -0,0 +1,81 @@
""" ES2 Pilatus Curtain"""
import time
from ophyd import Component as Cpt
from ophyd import Device, Kind, EpicsSignal, EpicsSignalRO
from ophyd_devices.utils import bec_utils
class GasMixSetup(Device):
"""Class for the ES2 Pilatus Curtain"""
USER_ACCESS = ['open', 'close']
open_cover = Cpt(
EpicsSignal, suffix="OpenCover", kind="config", doc='Open Cover'
)
close_cover = Cpt(
EpicsSignal, suffix="CloseCover", kind="config", doc='Close Cover'
)
cover_is_closed = Cpt(
EpicsSignalRO, suffix="CoverIsClosed", kind="config", doc='Cover is closed'
)
cover_is_open = Cpt(
EpicsSignalRO, suffix="CoverIsOpen", kind="config", doc='Cover is open'
)
cover_is_moving = Cpt(
EpicsSignalRO, suffix="CoverIsMoving", kind="config", doc='Cover is moving'
)
cover_error = Cpt(
EpicsSignalRO, suffix="CoverError", kind="config", doc='Cover error'
)
def __init__(
self, prefix="", *, name: str, kind: Kind = None, device_manager=None, parent=None, **kwargs
):
"""Initialize the Pilatus Curtain.
Args:
prefix (str): EPICS prefix for the device
name (str): Name of the device
kind (Kind): Kind of the device
device_manager (DeviceManager): Device manager instance
parent (Device): Parent device
kwargs: Additional keyword arguments
"""
super().__init__(prefix, name=name, kind=kind, parent=parent, **kwargs)
self.device_manager = device_manager
self.service_cfg = None
self.timeout_for_pvwait = 30
self.readback.name = self.name
# Wait for connection on all components, ensure IOC is connected
self.wait_for_connection(all_signals=True, timeout=5)
if device_manager:
self.device_manager = device_manager
else:
self.device_manager = bec_utils.DMMock()
self.connector = self.device_manager.connector
def open(self) -> None:
"""Open the cover"""
self.open_cover.put(1)
while not self.cover_is_open.get():
time.sleep(0.1)
if self.cover_error.get():
raise TimeoutError('Curtain did not open successfully and is now in an error state')
def close(self) -> None:
"""Close the cover"""
self.close_cover.put(1)
while not self.cover_is_closed.get():
time.sleep(0.1)
if self.cover_error.get():
raise TimeoutError('Curtain did not close successfully and is now in an error state')