diff --git a/debye_bec/device_configs/x01da_beam_monitors.yaml b/debye_bec/device_configs/x01da_beam_monitors.yaml new file mode 100644 index 0000000..d2e3a02 --- /dev/null +++ b/debye_bec/device_configs/x01da_beam_monitors.yaml @@ -0,0 +1,34 @@ + +################################### +## Beam Monitors ## +################################### + +beam_monitor_1: + readoutPriority: async + description: Beam monitor 1 + deviceClass: debye_bec.devices.cameras.prosilica_cam.ProsilicaCam + deviceConfig: + prefix: "X01DA-OP-GIGE01:" + onFailure: retry + enabled: true + softwareTrigger: false + +beam_monitor_2: + readoutPriority: async + description: Beam monitor 2 + deviceClass: debye_bec.devices.cameras.prosilica_cam.ProsilicaCam + deviceConfig: + prefix: "X01DA-OP-GIGE02:" + onFailure: retry + enabled: true + softwareTrigger: false + +xray_eye: + readoutPriority: async + description: X-ray eye + deviceClass: debye_bec.devices.cameras.basler_cam.BaslerCam + deviceConfig: + prefix: "X01DA-ES-XRAYEYE:" + onFailure: retry + enabled: true + softwareTrigger: false diff --git a/debye_bec/device_configs/x01da_database.yaml b/debye_bec/device_configs/x01da_database.yaml deleted file mode 100644 index 7ef4b68..0000000 --- a/debye_bec/device_configs/x01da_database.yaml +++ /dev/null @@ -1,875 +0,0 @@ -################### -#### FRONT END #### -################### - -## Slit Diaphragm -- Physical positioners -sldi_trxr: - readoutPriority: baseline - description: Front-end slit diaphragm X-translation Ring-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:TRXR - onFailure: retry - enabled: true - softwareTrigger: false -sldi_trxw: - readoutPriority: baseline - description: Front-end slit diaphragm X-translation Wall-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:TRXW - onFailure: retry - enabled: true - softwareTrigger: false -sldi_tryb: - readoutPriority: baseline - description: Front-end slit diaphragm Y-translation Bottom-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:TRYB - onFailure: retry - enabled: true - softwareTrigger: false -sldi_tryt: - readoutPriority: baseline - description: Front-end slit diaphragm X-translation Top-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:TRYT - onFailure: retry - enabled: true - softwareTrigger: false - -## Slit Diaphragm -- Virtual positioners - -sldi_centerx: - readoutPriority: baseline - description: Front-end slit diaphragm X-center - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:CENTERX - onFailure: retry - enabled: true - softwareTrigger: false -sldi_gapx: - readoutPriority: baseline - description: Front-end slit diaphragm X-gap - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:GAPX - onFailure: retry - enabled: true - softwareTrigger: false -sldi_centery: - readoutPriority: baseline - description: Front-end slit diaphragm Y-center - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:CENTERY - onFailure: retry - enabled: true - softwareTrigger: false -sldi_gapy: - readoutPriority: baseline - description: Front-end slit diaphragm Y-gap - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:GAPY - onFailure: retry - enabled: true - softwareTrigger: false - -## Collimating Mirror -- Physical Positioners - -cm_trxu: - readoutPriority: baseline - description: Collimating Mirror X-translation upstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:TRXU - onFailure: retry - enabled: true - softwareTrigger: false -cm_trxd: - readoutPriority: baseline - description: Collimating Mirror X-translation downstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:TRXD - onFailure: retry - enabled: true - softwareTrigger: false -cm_tryu: - readoutPriority: baseline - description: Collimating Mirror Y-translation upstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:TRYU - onFailure: retry - enabled: true - softwareTrigger: false -cm_trydr: - readoutPriority: baseline - description: Collimating Mirror Y-translation downstream ring - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:TRYDR - onFailure: retry - enabled: true - softwareTrigger: false -cm_trydw: - readoutPriority: baseline - description: Collimating Mirror Y-translation downstream wall - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:TRYDW - onFailure: retry - enabled: true - softwareTrigger: false -cm_bnd: - readoutPriority: baseline - description: Collimating Mirror bender - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:BND - onFailure: retry - enabled: true - softwareTrigger: false - -## Collimating Mirror -- Virtual Positioners - -cm_rotx: - readoutPriority: baseline - description: Collimating Morror Pitch - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:ROTX - onFailure: retry - enabled: true - softwareTrigger: false -cm_roty: - readoutPriority: baseline - description: Collimating Morror Yaw - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:ROTY - onFailure: retry - enabled: true - softwareTrigger: false -cm_rotz: - readoutPriority: baseline - description: Collimating Morror Roll - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:ROTZ - onFailure: retry - enabled: true - softwareTrigger: false -cm_xctp: - readoutPriority: baseline - description: Collimating Morror Center Point X - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:XTCP - onFailure: retry - enabled: true - softwareTrigger: false -cm_ytcp: - readoutPriority: baseline - description: Collimating Morror Center Point Y - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:YTCP - onFailure: retry - enabled: true - softwareTrigger: false -cm_ztcp: - readoutPriority: baseline - description: Collimating Morror Center Point Z - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:ZTCP - onFailure: retry - enabled: true - softwareTrigger: false -cm_xstripe: - readoutPriority: baseline - description: Collimating Morror X Stripe - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:XSTRIPE - onFailure: retry - enabled: true - softwareTrigger: false - -################### -###### OPTICS ##### -################### - -## Bragg Monochromator -mo1_bragg: - readoutPriority: baseline - description: Positioner for the Monochromator - deviceClass: debye_bec.devices.mo1_bragg.mo1_bragg.Mo1Bragg - deviceConfig: - prefix: "X01DA-OP-MO1:BRAGG:" - onFailure: retry - enabled: true - softwareTrigger: false - -## Monochromator -- Physical Positioners - -mo_try: - readoutPriority: baseline - description: Monochromator Y Translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-MO1:TRY - onFailure: retry - enabled: true - softwareTrigger: false -mo_trx: - readoutPriority: baseline - description: Monochromator X Translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-MO1:TRY - onFailure: retry - enabled: true - softwareTrigger: false -mo_roty: - readoutPriority: baseline - description: Monochromator Yaw - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-MO1:ROTY - onFailure: retry - enabled: true - softwareTrigger: false - -## Focusing Mirror -- Physical Positioners - -fm_trxu: - readoutPriority: baseline - description: Focusing Mirror X-translation upstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:TRXU - onFailure: retry - enabled: true - softwareTrigger: false -fm_trxd: - readoutPriority: baseline - description: Focusing Mirror X-translation downstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:TRXD - onFailure: retry - enabled: true - softwareTrigger: false -fm_tryd: - readoutPriority: baseline - description: Focusing Mirror Y-translation downstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:TRYD - onFailure: retry - enabled: true - softwareTrigger: false -fm_tryur: - readoutPriority: baseline - description: Focusing Mirror Y-translation upstream ring - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:TRYUR - onFailure: retry - enabled: true - softwareTrigger: false -fm_tryuw: - readoutPriority: baseline - description: Focusing Mirror Y-translation upstream wall - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:TRYUW - onFailure: retry - enabled: true - softwareTrigger: false -fm_bnd: - readoutPriority: baseline - description: Focusing Mirror bender - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:BND - onFailure: retry - enabled: true - softwareTrigger: false - -## Focusing Mirror -- Virtual Positioners - -fm_rotx: - readoutPriority: baseline - description: Focusing Morror Pitch - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:ROTX - onFailure: retry - enabled: true - softwareTrigger: false -fm_roty: - readoutPriority: baseline - description: Focusing Morror Yaw - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:ROTY - onFailure: retry - enabled: true - softwareTrigger: false -fm_rotz: - readoutPriority: baseline - description: Focusing Morror Roll - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:ROTZ - onFailure: retry - enabled: true - softwareTrigger: false -fm_xctp: - readoutPriority: baseline - description: Focusing Morror Center Point X - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:XTCP - onFailure: retry - enabled: true - softwareTrigger: false -fm_ytcp: - readoutPriority: baseline - description: Focusing Morror Center Point Y - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:YTCP - onFailure: retry - enabled: true - softwareTrigger: false -fm_ztcp: - readoutPriority: baseline - description: Focusing Morror Center Point Z - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-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 - -## Optics Slits 1 -- Physical positioners - -sl1_trxr: - readoutPriority: baseline - description: Optics slits 1 X-translation Ring-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL1:TRXR - onFailure: retry - enabled: true - softwareTrigger: false -sl1_trxw: - readoutPriority: baseline - description: Optics slits 1 X-translation Wall-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL1:TRXW - onFailure: retry - enabled: true - softwareTrigger: false -sl1_tryb: - readoutPriority: baseline - description: Optics slits 1 Y-translation Bottom-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL1:TRYB - onFailure: retry - enabled: true - softwareTrigger: false -sl1_tryt: - readoutPriority: baseline - description: Optics slits 1 X-translation Top-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL1:TRYT - onFailure: retry - enabled: true - softwareTrigger: false -bm1_try: - readoutPriority: baseline - description: Beam Monitor 1 Y-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-BM1:TRY - onFailure: retry - enabled: true - softwareTrigger: false - -## Optics Slits 1 -- Virtual positioners - -sl1_centerx: - readoutPriority: baseline - description: Optics slits 1 X-center - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL1:CENTERX - onFailure: retry - enabled: true - softwareTrigger: false -sl1_gapx: - readoutPriority: baseline - description: Optics slits 1 X-gap - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL1:GAPX - onFailure: retry - enabled: true - softwareTrigger: false -sl1_centery: - readoutPriority: baseline - description: Optics slits 1 Y-center - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL1:CENTERY - onFailure: retry - enabled: true - softwareTrigger: false -sl1_gapy: - readoutPriority: baseline - description: Optics slits 1 Y-gap - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL1:GAPY - onFailure: retry - enabled: true - softwareTrigger: false - -## Optics Slits 2 -- Physical positioners - -sl2_trxr: - readoutPriority: baseline - description: Optics slits 2 X-translation Ring-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL2:TRXR - onFailure: retry - enabled: true - softwareTrigger: false -sl2_trxw: - readoutPriority: baseline - description: Optics slits 2 X-translation Wall-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL2:TRXW - onFailure: retry - enabled: true - softwareTrigger: false -sl2_tryb: - readoutPriority: baseline - description: Optics slits 2 Y-translation Bottom-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL2:TRYB - onFailure: retry - enabled: true - softwareTrigger: false -sl2_tryt: - readoutPriority: baseline - description: Optics slits 2 X-translation Top-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL2:TRYT - onFailure: retry - enabled: true - softwareTrigger: false -bm2_try: - readoutPriority: baseline - description: Beam Monitor 2 Y-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-BM2:TRY - onFailure: retry - enabled: true - softwareTrigger: false - -## Optics Slits 2 -- Virtual positioners - -sl2_centerx: - readoutPriority: baseline - description: Optics slits 2 X-center - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL2:CENTERX - onFailure: retry - enabled: true - softwareTrigger: false -sl2_gapx: - readoutPriority: baseline - description: Optics slits 2 X-gap - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL2:GAPX - onFailure: retry - enabled: true - softwareTrigger: false -sl2_centery: - readoutPriority: baseline - description: Optics slits 2 Y-center - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL2:CENTERY - onFailure: retry - enabled: true - softwareTrigger: false -sl2_gapy: - readoutPriority: baseline - description: Optics slits 2 Y-gap - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-SL2:GAPY - onFailure: retry - enabled: true - softwareTrigger: false - -############################### -###### EXPERIMENTAL HUTCH ##### -############################### - -########################################### -## Optical Table -- Physical Positioners ## -########################################### - -ot_tryu: - readoutPriority: baseline - description: Optical Table Y-Translation Upstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES-OT:TRYU - onFailure: retry - enabled: true - softwareTrigger: false -ot_tryd: - readoutPriority: baseline - description: Optical Table Y-Translation Downstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES-OT:TRYD - onFailure: retry - enabled: true - softwareTrigger: false - -############################################ -## Optical Table -- Virtual Positioners ### -############################################ - -ot_try: - readoutPriority: baseline - description: Optical Table Y-Translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES-OT:TRY - onFailure: retry - enabled: true - softwareTrigger: false -ot_pitch: - readoutPriority: baseline - description: Optical Table Pitch - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES-OT:ROTX - onFailure: retry - enabled: true - softwareTrigger: false - -######################################### -## Exit Window -- Physical Positioners ## -######################################### - -es0wi_try: - readoutPriority: baseline - description: End Station 0 Exit Window Y-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES0-WI:TRY - onFailure: retry - enabled: true - softwareTrigger: false - -############################################### -## End Station Slits -- Physical Positioners ## -############################################### - -es0sl_trxr: - readoutPriority: baseline - description: End Station slits X-translation Ring-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES0-SL:TRXR - onFailure: retry - enabled: true - softwareTrigger: false -es0sl_trxw: - readoutPriority: baseline - description: End Station slits X-translation Wall-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES0-SL:TRXW - onFailure: retry - enabled: true - softwareTrigger: false -es0sl_tryb: - readoutPriority: baseline - description: End Station slits Y-translation Bottom-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES0-SL:TRYB - onFailure: retry - enabled: true - softwareTrigger: false -es0sl_tryt: - readoutPriority: baseline - description: End Station slits X-translation Top-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES0-SL:TRYT - onFailure: retry - enabled: true - softwareTrigger: false - -############################################## -## End Station Slits -- Virtual positioners ## -############################################## - -es0sl_center: - readoutPriority: baseline - description: End Station slits X-center - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES0-SL:CENTERX - onFailure: retry - enabled: true - softwareTrigger: false -es0sl_gapx: - readoutPriority: baseline - description: End Station slits X-gap - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES0-SL:GAPX - onFailure: retry - enabled: true - softwareTrigger: false -es0sl_centery: - readoutPriority: baseline - description: End Station slits Y-center - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES0-SL:CENTERY - onFailure: retry - enabled: true - softwareTrigger: false -es0sl_gapy: - readoutPriority: baseline - description: End Station slits Y-gap - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES0-SL:GAPY - onFailure: retry - enabled: true - softwareTrigger: false - -######################################################### -## Pinhole and alignment laser -- Physical Positioners ## -######################################################### - -es1pin_try: - readoutPriority: baseline - description: End Station pinhole and alignment laser Y-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-PIN1:TRY - onFailure: retry - enabled: true - softwareTrigger: false -es1pin_trx: - readoutPriority: baseline - description: End Station pinhole and alignment laser X-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-PIN1:TRX - onFailure: retry - enabled: true - softwareTrigger: false -es1pin_rotx: - readoutPriority: baseline - description: End Station pinhole and alignment laser X-rotation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-PIN1:ROTX - onFailure: retry - enabled: true - softwareTrigger: false -es1pin_roty: - readoutPriority: baseline - description: End Station pinhole and alignment laser Y-rotation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-PIN1:ROTY - onFailure: retry - enabled: true - softwareTrigger: false - - -################################################ -## Sample Manipulator -- Physical Positioners ## -################################################ - -es1man_trx: - readoutPriority: baseline - description: End Station sample manipulator X-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-MAN1:TRX - onFailure: retry - enabled: true - softwareTrigger: false -es1man_try: - readoutPriority: baseline - description: End Station sample manipulator Y-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-MAN1:TRY - onFailure: retry - enabled: true - softwareTrigger: false -es1man_trz: - readoutPriority: baseline - description: End Station sample manipulator Z-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-MAN1:TRZ - onFailure: retry - enabled: true - softwareTrigger: false -es1man_roty: - readoutPriority: baseline - description: End Station sample manipulator Y-rotation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-MAN1:ROTY - onFailure: retry - enabled: true - softwareTrigger: false - -############################################ -## Segemented Arc -- Physical Positioners ## -############################################ - -es1arc_roty: - readoutPriority: baseline - description: End Station segmented arc Y-rotation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-ARC:ROTY - onFailure: retry - enabled: true - softwareTrigger: false -es1det1_trx: - readoutPriority: baseline - description: End Station SDD 1 X-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-DET1:TRX - onFailure: retry - enabled: true - softwareTrigger: false -es1bm1_trx: - readoutPriority: baseline - description: End Station X-ray Eye X-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-BM1:TRX - onFailure: retry - enabled: true - softwareTrigger: false -es1det2_trx: - readoutPriority: baseline - description: End Station SDD 2 X-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-DET2:TRX - onFailure: retry - enabled: true - softwareTrigger: false - -####################################### -## Beam Stop -- Physical Positioners ## -####################################### - -es2bs_trx: - readoutPriority: baseline - description: End Station beamstop X-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES2-BS:TRX - onFailure: retry - enabled: true - softwareTrigger: false -es2bs_try: - readoutPriority: baseline - description: End Station beamstop Y-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES2-BS:TRY - onFailure: retry - enabled: true - softwareTrigger: false - -############################################## -## IC12 Manipulator -- Physical Positioners ## -############################################## - -es2ma2_try: - readoutPriority: baseline - description: End Station ionization chamber 1+2 Y-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES2-MA2:TRY - onFailure: retry - enabled: true - softwareTrigger: false -es2ma2_trz: - readoutPriority: baseline - description: End Station ionization chamber 1+2 Z-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES2-MA2:TRZ - onFailure: retry - enabled: true - softwareTrigger: false - -####################################################### -## XRD Detector Manipulator -- Physical Positioners ## -####################################################### - -es2ma3_try: - readoutPriority: baseline - description: End Station XRD detector Y-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES2-MA3:TRY - onFailure: retry - enabled: true - softwareTrigger: false diff --git a/debye_bec/device_configs/x01da_experimental_hutch.yaml b/debye_bec/device_configs/x01da_experimental_hutch.yaml new file mode 100644 index 0000000..f14d1aa --- /dev/null +++ b/debye_bec/device_configs/x01da_experimental_hutch.yaml @@ -0,0 +1,389 @@ +################################### +## Optical Table ## +################################### + +ot_tryu: + readoutPriority: baseline + description: Optical Table Y-Translation Upstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES-OT:TRYU + onFailure: retry + enabled: true + softwareTrigger: false + +ot_tryd: + readoutPriority: baseline + description: Optical Table Y-Translation Downstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES-OT:TRYD + onFailure: retry + enabled: true + softwareTrigger: false + +ot_es1_trz: + readoutPriority: baseline + description: Optical Table ES1 Z-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-OT:TRZ + onFailure: retry + enabled: true + softwareTrigger: false + +ot_es2_trz: + readoutPriority: baseline + description: Optical Table ES2 Z-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES2-OT:TRZ + onFailure: retry + enabled: true + softwareTrigger: false + +ot_try: + readoutPriority: baseline + description: Optical Table Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES-OT:TRY + onFailure: retry + enabled: true + softwareTrigger: false + +ot_pitch: + readoutPriority: baseline + description: Optical Table Pitch + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES-OT:ROTX + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## Exit Window ## +################################### + +es0wi_try: + readoutPriority: baseline + description: End Station 0 Exit Window Y-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES0-WI:TRY + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## ES0 Filter ## +################################### + +es0filter: + readoutPriority: baseline + description: ES0 filter station + deviceClass: debye_bec.devices.es0filter.ES0Filter + deviceConfig: + prefix: "X01DA-ES0-FI:" + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## Slits ES0 ## +################################### + +es0sl_trxr: + readoutPriority: baseline + description: End Station slits X-translation Ring-edge + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES0-SL:TRXR + onFailure: retry + enabled: true + softwareTrigger: false + +es0sl_trxw: + readoutPriority: baseline + description: End Station slits X-translation Wall-edge + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES0-SL:TRXW + onFailure: retry + enabled: true + softwareTrigger: false + +es0sl_tryb: + readoutPriority: baseline + description: End Station slits Y-translation Bottom-edge + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES0-SL:TRYB + onFailure: retry + enabled: true + softwareTrigger: false + +es0sl_tryt: + readoutPriority: baseline + description: End Station slits X-translation Top-edge + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES0-SL:TRYT + onFailure: retry + enabled: true + softwareTrigger: false + +es0sl_center: + readoutPriority: baseline + description: End Station slits X-center + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES0-SL:CENTERX + onFailure: retry + enabled: true + softwareTrigger: false + +es0sl_gapx: + readoutPriority: baseline + description: End Station slits X-gap + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES0-SL:GAPX + onFailure: retry + enabled: true + softwareTrigger: false + +es0sl_centery: + readoutPriority: baseline + description: End Station slits Y-center + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES0-SL:CENTERY + onFailure: retry + enabled: true + softwareTrigger: false + +es0sl_gapy: + readoutPriority: baseline + description: End Station slits Y-gap + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES0-SL:GAPY + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## Alignment Laser ## +################################### + +es1_alignment_laser: + readoutPriority: baseline + description: ES1 alignment laser + deviceClass: ophyd.EpicsSignal + deviceConfig: + read_pv: "X01DA-ES1-LAS:Relay" + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## Sample Manipulator ## +################################### + +es1man_trx: + readoutPriority: baseline + description: End Station sample manipulator X-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-MAN1:TRX + onFailure: retry + enabled: true + softwareTrigger: false + +es1man_try: + readoutPriority: baseline + description: End Station sample manipulator Y-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-MAN1:TRY + onFailure: retry + enabled: true + softwareTrigger: false + +es1man_trz: + readoutPriority: baseline + description: End Station sample manipulator Z-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-MAN1:TRZ + onFailure: retry + enabled: true + softwareTrigger: false + +es1man_roty: + readoutPriority: baseline + description: End Station sample manipulator Y-rotation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-MAN1:ROTY + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## Segmented Arc ## +################################### + +es1arc_roty: + readoutPriority: baseline + description: End Station segmented arc Y-rotation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-ARC:ROTY + onFailure: retry + enabled: true + softwareTrigger: false + +es1det1_trx: + readoutPriority: baseline + description: End Station SDD 1 X-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-DET1:TRX + onFailure: retry + enabled: true + softwareTrigger: false + +es1bm1_trx: + readoutPriority: baseline + description: End Station X-ray Eye X-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-BM1:TRX + onFailure: retry + enabled: true + softwareTrigger: false + +es1det2_trx: + readoutPriority: baseline + description: End Station SDD 2 X-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-DET2:TRX + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## IC1 + IC2 Manipulator ## +################################### + +es2ma2_try: + readoutPriority: baseline + description: End Station ionization chamber 1+2 Y-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES2-MA2:TRY + onFailure: retry + enabled: true + softwareTrigger: false + +es2ma2_trz: + readoutPriority: baseline + description: End Station ionization chamber 1+2 Z-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES2-MA2:TRZ + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## XRD Detector Manipulator ## +################################### + +es2ma3_try: + readoutPriority: baseline + description: End Station XRD detector Y-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES2-MA3:TRY + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## Hutch Env. Sensors + Light ## +################################### + +es_temperature1: + readoutPriority: baseline + 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: baseline + 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: baseline + 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: baseline + 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: baseline + 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: baseline + 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: baseline + description: ES light toggle + deviceClass: ophyd.EpicsSignal + deviceConfig: + read_pv: "X01DA-EH-LIGHT:TOGGLE" + onFailure: retry + enabled: true + softwareTrigger: false \ No newline at end of file diff --git a/debye_bec/device_configs/x01da_frontend.yaml b/debye_bec/device_configs/x01da_frontend.yaml new file mode 100644 index 0000000..bc097a3 --- /dev/null +++ b/debye_bec/device_configs/x01da_frontend.yaml @@ -0,0 +1,218 @@ + +################################### +## Frontend Slits ## +################################### + +sldi_trxr: + readoutPriority: baseline + description: Front-end slit diaphragm X-translation Ring-edge + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-SLDI:TRXR + onFailure: retry + enabled: true + softwareTrigger: false + +sldi_trxw: + readoutPriority: baseline + description: Front-end slit diaphragm X-translation Wall-edge + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-SLDI:TRXW + onFailure: retry + enabled: true + softwareTrigger: false + +sldi_tryb: + readoutPriority: baseline + description: Front-end slit diaphragm Y-translation Bottom-edge + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-SLDI:TRYB + onFailure: retry + enabled: true + softwareTrigger: false + +sldi_tryt: + readoutPriority: baseline + description: Front-end slit diaphragm X-translation Top-edge + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-SLDI:TRYT + onFailure: retry + enabled: true + softwareTrigger: false + +sldi_centerx: + readoutPriority: baseline + description: Front-end slit diaphragm X-center + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-SLDI:CENTERX + onFailure: retry + enabled: true + softwareTrigger: false + +sldi_gapx: + readoutPriority: baseline + description: Front-end slit diaphragm X-gap + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-SLDI:GAPX + onFailure: retry + enabled: true + softwareTrigger: false + +sldi_centery: + readoutPriority: baseline + description: Front-end slit diaphragm Y-center + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-SLDI:CENTERY + onFailure: retry + enabled: true + softwareTrigger: false + +sldi_gapy: + readoutPriority: baseline + description: Front-end slit diaphragm Y-gap + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-SLDI:GAPY + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## Collimating Mirror ## +################################### + +cm_trxu: + readoutPriority: baseline + description: Collimating Mirror X-translation upstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:TRXU + onFailure: retry + enabled: true + softwareTrigger: false + +cm_trxd: + readoutPriority: baseline + description: Collimating Mirror X-translation downstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:TRXD + onFailure: retry + enabled: true + softwareTrigger: false + +cm_tryu: + readoutPriority: baseline + description: Collimating Mirror Y-translation upstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:TRYU + onFailure: retry + enabled: true + softwareTrigger: false + +cm_trydr: + readoutPriority: baseline + description: Collimating Mirror Y-translation downstream ring + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:TRYDR + onFailure: retry + enabled: true + softwareTrigger: false + +cm_trydw: + readoutPriority: baseline + description: Collimating Mirror Y-translation downstream wall + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:TRYDW + onFailure: retry + enabled: true + softwareTrigger: false + +cm_bnd: + readoutPriority: baseline + description: Collimating Mirror bender + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:BND + onFailure: retry + enabled: true + softwareTrigger: false + +cm_rotx: + readoutPriority: baseline + description: Collimating Morror Pitch + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:ROTX + onFailure: retry + enabled: true + softwareTrigger: false + +cm_roty: + readoutPriority: baseline + description: Collimating Morror Yaw + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:ROTY + onFailure: retry + enabled: true + softwareTrigger: false + +cm_rotz: + readoutPriority: baseline + description: Collimating Morror Roll + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:ROTZ + onFailure: retry + enabled: true + softwareTrigger: false + +cm_trx: + readoutPriority: baseline + description: Collimating Morror Center Point X + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:XTCP + onFailure: retry + enabled: true + softwareTrigger: false + +cm_try: + readoutPriority: baseline + description: Collimating Morror Center Point Y + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:YTCP + onFailure: retry + enabled: true + softwareTrigger: false + +cm_ztcp: + readoutPriority: baseline + description: Collimating Morror Center Point Z + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:ZTCP + onFailure: retry + enabled: true + softwareTrigger: false + +cm_xstripe: + readoutPriority: baseline + description: Collimating Morror X Stripe + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-FE-CM:XSTRIPE + onFailure: retry + enabled: true + softwareTrigger: false \ No newline at end of file diff --git a/debye_bec/device_configs/x01da_machine.yaml b/debye_bec/device_configs/x01da_machine.yaml index fe1ae5d..cd957dd 100644 --- a/debye_bec/device_configs/x01da_machine.yaml +++ b/debye_bec/device_configs/x01da_machine.yaml @@ -1,3 +1,8 @@ + +################################### +## SLS Machine ## +################################### + curr: readoutPriority: baseline description: SLS ring current diff --git a/debye_bec/device_configs/x01da_optic_slits.yaml b/debye_bec/device_configs/x01da_optics.yaml similarity index 55% rename from debye_bec/device_configs/x01da_optic_slits.yaml rename to debye_bec/device_configs/x01da_optics.yaml index 0b364f7..6d77341 100644 --- a/debye_bec/device_configs/x01da_optic_slits.yaml +++ b/debye_bec/device_configs/x01da_optics.yaml @@ -1,4 +1,41 @@ -## Optics Slits 1 -- Physical positioners + +################################### +## Monochromator ## +################################### + +mo_try: + readoutPriority: baseline + description: Monochromator Y Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-MO1:TRY + onFailure: retry + enabled: true + softwareTrigger: false + +mo_trx: + readoutPriority: baseline + description: Monochromator X Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-MO1:TRX + onFailure: retry + enabled: true + softwareTrigger: false + +mo_roty: + readoutPriority: baseline + description: Monochromator Yaw + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-MO1:ROTY + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## Optics Slits + Beam Monitor 1 ## +################################### sl1_trxr: readoutPriority: baseline @@ -12,6 +49,7 @@ sl1_trxr: deviceTags: - optics - slits + sl1_trxw: readoutPriority: baseline description: Optics slits 1 X-translation Wall-edge @@ -24,6 +62,7 @@ sl1_trxw: deviceTags: - optics - slits + sl1_tryb: readoutPriority: baseline description: Optics slits 1 Y-translation Bottom-edge @@ -36,6 +75,7 @@ sl1_tryb: deviceTags: - optics - slits + sl1_tryt: readoutPriority: baseline description: Optics slits 1 X-translation Top-edge @@ -48,6 +88,7 @@ sl1_tryt: deviceTags: - optics - slits + bm1_try: readoutPriority: baseline description: Beam Monitor 1 Y-translation @@ -61,8 +102,6 @@ bm1_try: - optics - slits -## Optics Slits 1 -- Virtual positioners - sl1_centerx: readoutPriority: baseline description: Optics slits 1 X-center @@ -75,6 +114,7 @@ sl1_centerx: deviceTags: - optics - slits + sl1_gapx: readoutPriority: baseline description: Optics slits 1 X-gap @@ -87,6 +127,7 @@ sl1_gapx: deviceTags: - optics - slits + sl1_centery: readoutPriority: baseline description: Optics slits 1 Y-center @@ -99,6 +140,7 @@ sl1_centery: deviceTags: - optics - slits + sl1_gapy: readoutPriority: baseline description: Optics slits 1 Y-gap @@ -112,7 +154,128 @@ sl1_gapy: - optics - slits -## Optics Slits 2 -- Physical positioners +################################### +## Focusing Mirror ## +################################### + +fm_trxu: + readoutPriority: baseline + description: Focusing Mirror X-translation upstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-FM:TRXU + onFailure: retry + enabled: true + softwareTrigger: false +fm_trxd: + readoutPriority: baseline + description: Focusing Mirror X-translation downstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-FM:TRXD + onFailure: retry + enabled: true + softwareTrigger: false +fm_tryd: + readoutPriority: baseline + description: Focusing Mirror Y-translation downstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-FM:TRYD + onFailure: retry + enabled: true + softwareTrigger: false +fm_tryur: + readoutPriority: baseline + description: Focusing Mirror Y-translation upstream ring + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-FM:TRYUR + onFailure: retry + enabled: true + softwareTrigger: false +fm_tryuw: + readoutPriority: baseline + description: Focusing Mirror Y-translation upstream wall + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-FM:TRYUW + onFailure: retry + enabled: true + softwareTrigger: false +fm_bnd: + readoutPriority: baseline + description: Focusing Mirror bender + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-FM:BND + onFailure: retry + enabled: true + softwareTrigger: false + +fm_rotx: + readoutPriority: baseline + description: Focusing Morror Pitch + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-FM:ROTX + onFailure: retry + enabled: true + softwareTrigger: false + +fm_roty: + readoutPriority: baseline + description: Focusing Morror Yaw + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-FM:ROTY + onFailure: retry + enabled: true + softwareTrigger: false + +fm_rotz: + readoutPriority: baseline + description: Focusing Morror Roll + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-FM:ROTZ + onFailure: retry + enabled: true + softwareTrigger: false + +fm_xctp: + readoutPriority: baseline + description: Focusing Morror Center Point X + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-FM:XTCP + onFailure: retry + enabled: true + softwareTrigger: false + +fm_ytcp: + readoutPriority: baseline + description: Focusing Morror Center Point Y + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-FM:YTCP + onFailure: retry + enabled: true + softwareTrigger: false + +fm_ztcp: + readoutPriority: baseline + description: Focusing Morror Center Point Z + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-OP-FM:ZTCP + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## Optics Slits + Beam Monitor 2 ## +################################### sl2_trxr: readoutPriority: baseline @@ -126,6 +289,7 @@ sl2_trxr: deviceTags: - optics - slits + sl2_trxw: readoutPriority: baseline description: Optics slits 2 X-translation Wall-edge @@ -138,6 +302,7 @@ sl2_trxw: deviceTags: - optics - slits + sl2_tryb: readoutPriority: baseline description: Optics slits 2 Y-translation Bottom-edge @@ -150,6 +315,7 @@ sl2_tryb: deviceTags: - optics - slits + sl2_tryt: readoutPriority: baseline description: Optics slits 2 X-translation Top-edge @@ -162,6 +328,7 @@ sl2_tryt: deviceTags: - optics - slits + bm2_try: readoutPriority: baseline description: Beam Monitor 2 Y-translation @@ -175,8 +342,6 @@ bm2_try: - optics - slits -## Optics Slits 2 -- Virtual positioners - sl2_centerx: readoutPriority: baseline description: Optics slits 2 X-center @@ -189,6 +354,7 @@ sl2_centerx: deviceTags: - optics - slits + sl2_gapx: readoutPriority: baseline description: Optics slits 2 X-gap @@ -201,6 +367,7 @@ sl2_gapx: deviceTags: - optics - slits + sl2_centery: readoutPriority: baseline description: Optics slits 2 Y-center @@ -213,6 +380,7 @@ sl2_centery: deviceTags: - optics - slits + sl2_gapy: readoutPriority: baseline description: Optics slits 2 Y-gap diff --git a/debye_bec/device_configs/x01da_standard_config.yaml b/debye_bec/device_configs/x01da_standard_config.yaml new file mode 100644 index 0000000..5a0a701 --- /dev/null +++ b/debye_bec/device_configs/x01da_standard_config.yaml @@ -0,0 +1,75 @@ + +################################### +## General ## +################################### + +## SLS Machine +machine_config: + - !include ./x01da_machine.yaml + +## Beam Monitors OP + EH +beam_monitors_config: + - !include ./x01da_beam_monitors.yaml + +################################### +## Frontend ## +################################### + +## Frontend +frontend_config: + - !include ./x01da_frontend.yaml + +################################### +## Optics Hutch ## +################################### + +## Bragg Monochromator +mo1_bragg: + readoutPriority: monitored + description: Positioner for the Monochromator + deviceClass: debye_bec.devices.mo1_bragg.mo1_bragg.Mo1Bragg + deviceConfig: + prefix: "X01DA-OP-MO1:BRAGG:" + onFailure: retry + enabled: true + softwareTrigger: false +mo1_bragg_angle: + readoutPriority: baseline + description: Positioner for the Monochromator + deviceClass: debye_bec.devices.mo1_bragg.mo1_bragg_angle.Mo1BraggAngle + deviceConfig: + prefix: "X01DA-OP-MO1:BRAGG:" + onFailure: retry + enabled: true + softwareTrigger: false + +## Remaining optics hutch +optics_config: + - !include ./x01da_optics.yaml + +################################### +## Experimental Hutch ## +################################### + +## NIDAQ +nidaq: + readoutPriority: monitored + description: NIDAQ backend for data reading for debye scans + deviceClass: debye_bec.devices.nidaq.nidaq.Nidaq + deviceConfig: + prefix: "X01DA-PC-SCANSERVER:" + onFailure: retry + enabled: true + softwareTrigger: false + +## XAS (ICx, SDD, ref foils) +xas_config: + - !include ./x01da_xas.yaml + +## XRD (Pilatus, pinhole, beamstop) +xrd_config: + - !include ./x01da_xrd.yaml + +## Remaining experimental hutch +es_config: + - !include ./x01da_experimental_hutch.yaml \ No newline at end of file diff --git a/debye_bec/device_configs/x01da_test_config.yaml b/debye_bec/device_configs/x01da_test_config.yaml deleted file mode 100644 index 25c3fa1..0000000 --- a/debye_bec/device_configs/x01da_test_config.yaml +++ /dev/null @@ -1,636 +0,0 @@ -optic_slit_config: - - !include ./x01da_optic_slits.yaml -machine_config: - - !include ./x01da_machine.yaml -## Slit Diaphragm -- Physical positioners -sldi_trxr: - readoutPriority: baseline - description: Front-end slit diaphragm X-translation Ring-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:TRXR - onFailure: retry - enabled: true - softwareTrigger: false -sldi_trxw: - readoutPriority: baseline - description: Front-end slit diaphragm X-translation Wall-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:TRXW - onFailure: retry - enabled: true - softwareTrigger: false -sldi_tryb: - readoutPriority: baseline - description: Front-end slit diaphragm Y-translation Bottom-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:TRYB - onFailure: retry - enabled: true - softwareTrigger: false -sldi_tryt: - readoutPriority: baseline - description: Front-end slit diaphragm X-translation Top-edge - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:TRYT - onFailure: retry - enabled: true - softwareTrigger: false - -## Slit Diaphragm -- Virtual positioners - -sldi_centerx: - readoutPriority: baseline - description: Front-end slit diaphragm X-center - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:CENTERX - onFailure: retry - enabled: true - softwareTrigger: false -sldi_gapx: - readoutPriority: baseline - description: Front-end slit diaphragm X-gap - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:GAPX - onFailure: retry - enabled: true - softwareTrigger: false -sldi_centery: - readoutPriority: baseline - description: Front-end slit diaphragm Y-center - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:CENTERY - onFailure: retry - enabled: true - softwareTrigger: false -sldi_gapy: - readoutPriority: baseline - description: Front-end slit diaphragm Y-gap - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-SLDI:GAPY - onFailure: retry - enabled: true - softwareTrigger: false - - -## Collimating Mirror -- Physical Positioners - -cm_trxu: - readoutPriority: baseline - description: Collimating Mirror X-translation upstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:TRXU - onFailure: retry - enabled: true - softwareTrigger: false -cm_trxd: - readoutPriority: baseline - description: Collimating Mirror X-translation downstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:TRXD - onFailure: retry - enabled: true - softwareTrigger: false -cm_tryu: - readoutPriority: baseline - description: Collimating Mirror Y-translation upstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:TRYU - onFailure: retry - enabled: true - softwareTrigger: false -cm_trydr: - readoutPriority: baseline - description: Collimating Mirror Y-translation downstream ring - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:TRYDR - onFailure: retry - enabled: true - softwareTrigger: false -cm_trydw: - readoutPriority: baseline - description: Collimating Mirror Y-translation downstream wall - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:TRYDW - onFailure: retry - enabled: true - softwareTrigger: false -cm_bnd: - readoutPriority: baseline - description: Collimating Mirror bender - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:BND - onFailure: retry - enabled: true - softwareTrigger: false - -## Collimating Mirror -- Virtual Positioners - -cm_rotx: - readoutPriority: baseline - description: Collimating Morror Pitch - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:ROTX - onFailure: retry - enabled: true - softwareTrigger: false -cm_roty: - readoutPriority: baseline - description: Collimating Morror Yaw - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:ROTY - onFailure: retry - enabled: true - softwareTrigger: false -cm_rotz: - readoutPriority: baseline - description: Collimating Morror Roll - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:ROTZ - onFailure: retry - enabled: true - softwareTrigger: false -cm_trx: - readoutPriority: baseline - description: Collimating Morror Center Point X - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:XTCP - onFailure: retry - enabled: true - softwareTrigger: false -cm_try: - readoutPriority: baseline - description: Collimating Morror Center Point Y - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:YTCP - onFailure: retry - enabled: true - softwareTrigger: false -cm_ztcp: - readoutPriority: baseline - description: Collimating Morror Center Point Z - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:ZTCP - onFailure: retry - enabled: true - softwareTrigger: false -cm_xstripe: - readoutPriority: baseline - description: Collimating Morror X Stripe - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-FE-CM:XSTRIPE - onFailure: retry - enabled: true - softwareTrigger: false - -## Bragg Monochromator -mo1_bragg: - readoutPriority: baseline - description: Positioner for the Monochromator - deviceClass: debye_bec.devices.mo1_bragg.mo1_bragg.Mo1Bragg - deviceConfig: - prefix: "X01DA-OP-MO1:BRAGG:" - onFailure: retry - enabled: true - softwareTrigger: false -mo1_bragg_angle: - readoutPriority: baseline - description: Positioner for the Monochromator - deviceClass: debye_bec.devices.mo1_bragg.mo1_bragg_angle.Mo1BraggAngle - deviceConfig: - prefix: "X01DA-OP-MO1:BRAGG:" - onFailure: retry - enabled: true - softwareTrigger: false - -# NIDAQ -nidaq: - readoutPriority: monitored - description: NIDAQ backend for data reading for debye scans - deviceClass: debye_bec.devices.nidaq.nidaq.Nidaq - deviceConfig: - prefix: "X01DA-PC-SCANSERVER:" - onFailure: retry - enabled: true - softwareTrigger: false - -## Monochromator -- Physical Positioners - -mo_try: - readoutPriority: baseline - description: Monochromator Y Translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-MO1:TRY - onFailure: retry - enabled: true - softwareTrigger: false -mo_trx: - readoutPriority: baseline - description: Monochromator X Translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-MO1:TRX - onFailure: retry - enabled: true - softwareTrigger: false -mo_roty: - readoutPriority: baseline - description: Monochromator Yaw - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-MO1:ROTY - onFailure: retry - enabled: true - softwareTrigger: false - - ## Focusing Mirror -- Physical Positioners - -fm_trxu: - readoutPriority: baseline - description: Focusing Mirror X-translation upstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:TRXU - onFailure: retry - enabled: true - softwareTrigger: false -fm_trxd: - readoutPriority: baseline - description: Focusing Mirror X-translation downstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:TRXD - onFailure: retry - enabled: true - softwareTrigger: false -fm_tryd: - readoutPriority: baseline - description: Focusing Mirror Y-translation downstream - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:TRYD - onFailure: retry - enabled: true - softwareTrigger: false -fm_tryur: - readoutPriority: baseline - description: Focusing Mirror Y-translation upstream ring - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:TRYUR - onFailure: retry - enabled: true - softwareTrigger: false -fm_tryuw: - readoutPriority: baseline - description: Focusing Mirror Y-translation upstream wall - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:TRYUW - onFailure: retry - enabled: true - softwareTrigger: false -fm_bnd: - readoutPriority: baseline - description: Focusing Mirror bender - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:BND - onFailure: retry - enabled: true - softwareTrigger: false - -## Focusing Mirror -- Virtual Positioners - -fm_rotx: - readoutPriority: baseline - description: Focusing Morror Pitch - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:ROTX - onFailure: retry - enabled: true - softwareTrigger: false -fm_roty: - readoutPriority: baseline - description: Focusing Morror Yaw - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:ROTY - onFailure: retry - enabled: true - softwareTrigger: false -fm_rotz: - readoutPriority: baseline - description: Focusing Morror Roll - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:ROTZ - onFailure: retry - enabled: true - softwareTrigger: false -fm_xctp: - readoutPriority: baseline - description: Focusing Morror Center Point X - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:XTCP - onFailure: retry - enabled: true - softwareTrigger: false -fm_ytcp: - readoutPriority: baseline - description: Focusing Morror Center Point Y - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:YTCP - onFailure: retry - enabled: true - softwareTrigger: false -fm_ztcp: - readoutPriority: baseline - description: Focusing Morror Center Point Z - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-OP-FM:ZTCP - onFailure: retry - enabled: true - softwareTrigger: false - -# Ionization Chambers - -ic0: - readoutPriority: baseline - description: Ionization chamber 0 - deviceClass: debye_bec.devices.ionization_chambers.ionization_chamber.IonizationChamber0 - deviceConfig: - prefix: "X01DA-" - onFailure: retry - enabled: true - softwareTrigger: false -ic1: - readoutPriority: baseline - description: Ionization chamber 1 - deviceClass: debye_bec.devices.ionization_chambers.ionization_chamber.IonizationChamber1 - deviceConfig: - prefix: "X01DA-" - onFailure: retry - enabled: true - softwareTrigger: false -ic2: - readoutPriority: baseline - description: Ionization chamber 2 - deviceClass: debye_bec.devices.ionization_chambers.ionization_chamber.IonizationChamber2 - deviceConfig: - prefix: "X01DA-" - onFailure: retry - enabled: true - softwareTrigger: false - -# ES0 Filter - -es0filter: - readoutPriority: baseline - description: ES0 filter station - deviceClass: debye_bec.devices.es0filter.ES0Filter - deviceConfig: - prefix: "X01DA-ES0-FI:" - onFailure: retry - enabled: true - softwareTrigger: false - -# Reference foil changer - -reffoilchanger: - readoutPriority: baseline - description: ES2 reference foil changer - deviceClass: debye_bec.devices.reffoilchanger.Reffoilchanger - deviceConfig: - prefix: "X01DA-" - onFailure: retry - enabled: true - softwareTrigger: false - -# Beam Monitors - -# beam_monitor_1: -# readoutPriority: async -# description: Beam monitor 1 -# deviceClass: debye_bec.devices.cameras.prosilica_cam.ProsilicaCam -# deviceConfig: -# prefix: "X01DA-OP-GIGE01:" -# onFailure: retry -# enabled: true -# softwareTrigger: false - -# beam_monitor_2: -# readoutPriority: async -# description: Beam monitor 2 -# deviceClass: debye_bec.devices.cameras.prosilica_cam.ProsilicaCam -# deviceConfig: -# prefix: "X01DA-OP-GIGE02:" -# onFailure: retry -# enabled: true -# softwareTrigger: false - -xray_eye: - readoutPriority: async - description: X-ray eye - deviceClass: debye_bec.devices.cameras.basler_cam.BaslerCam - deviceConfig: - prefix: "X01DA-ES-XRAYEYE:" - onFailure: retry - enabled: true - softwareTrigger: false - -# Pilatus Curtain -# pilatus_curtain: -# readoutPriority: baseline -# 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: baseline - 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: baseline - 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: baseline - 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: baseline - 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: baseline - 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: baseline - 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: baseline - 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: baseline - description: SDD1 temperature sensor - deviceClass: ophyd.EpicsSignalRO - deviceConfig: - read_pv: "X01DA-ES1-DET1:Temperature" - onFailure: retry - enabled: true - softwareTrigger: false - -sdd1_humidity: - readoutPriority: baseline - description: SDD1 humidity sensor - deviceClass: ophyd.EpicsSignalRO - deviceConfig: - read_pv: "X01DA-ES1-DET1:Humidity" - kind: "config" - onFailure: retry - enabled: true - softwareTrigger: false - -##################### -## Alignment Laser ## -##################### - -es1_alignment_laser: - readoutPriority: baseline - description: ES1 alignment laser - deviceClass: ophyd.EpicsSignal - deviceConfig: - read_pv: "X01DA-ES1-LAS:Relay" - onFailure: retry - enabled: true - softwareTrigger: false - -## Pinhole alignment stages -- Physical Positioners - -pin1_trx: - readoutPriority: baseline - description: Pinhole X-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-PIN1:TRX - onFailure: retry - enabled: true - softwareTrigger: false - tags: Endstation - -pin1_try: - readoutPriority: baseline - description: Pinhole Y-translation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-PIN1:TRY - onFailure: retry - enabled: true - softwareTrigger: false - tags: Endstation - -pin1_rotx: - readoutPriority: baseline - description: Pinhole X-rotation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-PIN1:ROTX - onFailure: retry - enabled: true - softwareTrigger: false - tags: Endstation - -pin1_roty: - readoutPriority: baseline - description: Pinhole Y-rotation - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: X01DA-ES1-PIN1:ROTY - onFailure: retry - enabled: true - softwareTrigger: false - tags: Endstation \ No newline at end of file diff --git a/debye_bec/device_configs/x01da_xas.yaml b/debye_bec/device_configs/x01da_xas.yaml new file mode 100644 index 0000000..42e2876 --- /dev/null +++ b/debye_bec/device_configs/x01da_xas.yaml @@ -0,0 +1,73 @@ + +################################### +## Ionization Chambers ## +################################### + +# ic0: +# readoutPriority: baseline +# description: Ionization chamber 0 +# deviceClass: debye_bec.devices.ionization_chambers.ionization_chamber.IonizationChamber0 +# deviceConfig: +# prefix: "X01DA-" +# onFailure: retry +# enabled: true +# softwareTrigger: false + +# ic1: +# readoutPriority: baseline +# description: Ionization chamber 1 +# deviceClass: debye_bec.devices.ionization_chambers.ionization_chamber.IonizationChamber1 +# deviceConfig: +# prefix: "X01DA-" +# onFailure: retry +# enabled: true +# softwareTrigger: false + +# ic2: +# readoutPriority: baseline +# description: Ionization chamber 2 +# deviceClass: debye_bec.devices.ionization_chambers.ionization_chamber.IonizationChamber2 +# deviceConfig: +# prefix: "X01DA-" +# onFailure: retry +# enabled: true +# softwareTrigger: false + +################################### +## Reference Foil Changer ## +################################### + +reffoilchanger: + readoutPriority: baseline + description: ES2 reference foil changer + deviceClass: debye_bec.devices.reffoilchanger.Reffoilchanger + deviceConfig: + prefix: "X01DA-" + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## SDD Sensors ## +################################### + +sdd1_temperature: + readoutPriority: baseline + description: SDD1 temperature sensor + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + read_pv: "X01DA-ES1-DET1:Temperature" + onFailure: retry + enabled: true + softwareTrigger: false + +sdd1_humidity: + readoutPriority: baseline + description: SDD1 humidity sensor + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + read_pv: "X01DA-ES1-DET1:Humidity" + kind: "config" + onFailure: retry + enabled: true + softwareTrigger: false \ No newline at end of file diff --git a/debye_bec/device_configs/x01da_xrd.yaml b/debye_bec/device_configs/x01da_xrd.yaml new file mode 100644 index 0000000..22ffdba --- /dev/null +++ b/debye_bec/device_configs/x01da_xrd.yaml @@ -0,0 +1,108 @@ + +################################### +## Pinhole ## +################################### + +pin1_trx: + readoutPriority: baseline + description: Pinhole X-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-PIN1:TRX + onFailure: retry + enabled: true + softwareTrigger: false + tags: Endstation + +pin1_try: + readoutPriority: baseline + description: Pinhole Y-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-PIN1:TRY + onFailure: retry + enabled: true + softwareTrigger: false + tags: Endstation + +pin1_rotx: + readoutPriority: baseline + description: Pinhole X-rotation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-PIN1:ROTX + onFailure: retry + enabled: true + softwareTrigger: false + tags: Endstation + +pin1_roty: + readoutPriority: baseline + description: Pinhole Y-rotation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES1-PIN1:ROTY + onFailure: retry + enabled: true + softwareTrigger: false + tags: Endstation + +################################### +## Beam Stop ## +################################### + +es2bs_trx: + readoutPriority: baseline + description: End Station beamstop X-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES2-BS:TRX + onFailure: retry + enabled: true + softwareTrigger: false + +es2bs_try: + readoutPriority: baseline + description: End Station beamstop Y-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X01DA-ES2-BS:TRY + onFailure: retry + enabled: true + softwareTrigger: false + +################################### +## Pilatus ## +################################### + +pilatus_curtain: + readoutPriority: baseline + description: Pilatus Curtain + deviceClass: debye_bec.devices.pilatus_curtain.PilatusCurtain + deviceConfig: + prefix: "X01DA-ES2-DET3:TRY-" + onFailure: retry + enabled: true + softwareTrigger: false + +pilatus: + readoutPriority: async + description: Pilatus + deviceClass: debye_bec.devices.pilatus.pilatus.Pilatus + deviceTags: + - detector + deviceConfig: + prefix: "X01DA-ES2-PIL:" + onFailure: retry + enabled: true + softwareTrigger: true + +# sampl_pil: +# readoutPriority: baseline +# description: Sample to pilatus distance +# deviceClass: ophyd.EpicsSignalRO +# deviceConfig: +# read_pv: "X01DA-SAMPL-PIL" +# onFailure: retry +# enabled: true +# softwareTrigger: false diff --git a/debye_bec/devices/ionization_chambers/ionization_chamber.py b/debye_bec/devices/ionization_chambers/ionization_chamber.py index 2b7bfd6..276f9b3 100644 --- a/debye_bec/devices/ionization_chambers/ionization_chamber.py +++ b/debye_bec/devices/ionization_chambers/ionization_chamber.py @@ -1,3 +1,5 @@ +"""Ionization chamber device class""" + from __future__ import annotations from typing import TYPE_CHECKING, Literal @@ -6,8 +8,9 @@ import numpy as np from ophyd import Component as Cpt from ophyd import Device from ophyd import DynamicDeviceComponent as Dcpt -from ophyd import EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV, Kind +from ophyd import EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV from ophyd.status import DeviceStatus, SubscriptionStatus +from ophyd_devices import CompareStatus, TransitionStatus from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase from typeguard import typechecked @@ -110,18 +113,10 @@ class IonizationChamber0(PSIDeviceBase): """ if self.amp.cOnOff.get() == AmplifierEnable.OFF: + status = CompareStatus(self.amp.cOnOff, AmplifierEnable.ON) + self.cancel_on_stop(status) self.amp.cOnOff.put(AmplifierEnable.ON) - - # Wait until channel is switched on - def _wait_enabled(): - return self.amp.cOnOff.get() == AmplifierEnable.ON - - if not self.wait_for_condition( - _wait_enabled, check_stopped=True, timeout=self.timeout_for_pvwait - ): - raise TimeoutError( - f"Enabling channel run into timeout after {self.timeout_for_pvwait} seconds" - ) + status.wait(self.timeout_for_pvwait) match gain: case "1e6": @@ -144,21 +139,13 @@ class IonizationChamber0(PSIDeviceBase): """Configure the filter setting of the specified channel Args: - value (Literal['1us', '3us', '10us', '30us', '100us', '300us', '1ms', '3ms']) : Desired filter + value (Literal['1us','3us','10us','30us','100us','300us','1ms','3ms']) :Desired filter """ if self.amp.cOnOff.get() == AmplifierEnable.OFF: + status = CompareStatus(self.amp.cOnOff, AmplifierEnable.ON) + self.cancel_on_stop(status) self.amp.cOnOff.put(AmplifierEnable.ON) - - # Wait until channel is switched on - def _wait_enabled(): - return self.amp.cOnOff.get() == AmplifierEnable.ON - - if not self.wait_for_condition( - _wait_enabled, check_stopped=True, timeout=self.timeout_for_pvwait - ): - raise TimeoutError( - f"Enabling channel run into timeout after {self.timeout_for_pvwait} seconds" - ) + status.wait(self.timeout_for_pvwait) match value: case "1us": @@ -187,20 +174,16 @@ class IonizationChamber0(PSIDeviceBase): hv (float) : Desired voltage for the 'HV' terminal. Voltage has to be between 0...3000 """ - if not (0 <= hv <= 3000): + if not 0 <= hv <= 3000: raise ValueError(f"specified HV {hv} not within range [0 .. 3000]") if not np.isclose(np.abs(hv - self.hv.grid_v.get()), 0, atol=3): raise ValueError(f"Grid {self.hv.grid_v.get()} must not be higher than HV {hv}!") if not self.hv_en.ena.get() == 1: - - def check_ch_ena(*, old_value, value, **kwargs): - return value == 1 - - status = SubscriptionStatus(device=self.hv_en.ena, callback=check_ch_ena) + status = CompareStatus(self.hv_en.ena, 1) + self.cancel_on_stop(status) self.hv_en.ena.put(1) - # Wait after setting ena to 1 - status.wait(timeout=2) + status.wait(self.timeout_for_pvwait) # Set current fixed to 3 mA (max) self.hv.hv_i.put(3) @@ -212,23 +195,20 @@ class IonizationChamber0(PSIDeviceBase): enable the high voltage (if external enable is active)! Args: - grid (float) : Desired voltage for the 'Grid' terminal, Grid Voltage has to be between 0...3000 + grid (float) : Desired voltage for the 'Grid' terminal, + Grid Voltage has to be between 0...3000 """ - if not (0 <= grid <= 3000): + if not 0 <= grid <= 3000: raise ValueError(f"specified Grid {grid} not within range [0 .. 3000]") if not np.isclose(np.abs(grid - self.hv.hv_v.get()), 0, atol=3): raise ValueError(f"Grid {grid} must not be higher than HV {self.hv.hv_v.get()}!") if not self.hv_en.ena.get() == 1: - - def check_ch_ena(*, old_value, value, **kwargs): - return value == 1 - - status = SubscriptionStatus(device=self.hv_en.ena, callback=check_ch_ena) + status = CompareStatus(self.hv_en.ena, 1) + self.cancel_on_stop(status) self.hv_en.ena.put(1) - # Wait after setting ena to 1 - status.wait(timeout=2) + status.wait(self.timeout_for_pvwait) # Set current fixed to 3 mA (max) self.hv.grid_i.put(3) @@ -244,7 +224,7 @@ class IonizationChamber0(PSIDeviceBase): pressure: float, *, wait: bool = False, - ) -> DeviceStatus: + ) -> DeviceStatus | None: """Fill an ionization chamber with the specified gas mixture. Args: @@ -256,13 +236,13 @@ class IonizationChamber0(PSIDeviceBase): wait (bool): If you like to wait for the filling to finish. """ - if not (0 <= conc1 <= 100): + if not 0 <= conc1 <= 100: raise ValueError(f"Concentration 1 {conc1} out of range [0 .. 100 %]") - if not (0 <= conc2 <= 100): + if not 0 <= conc2 <= 100: raise ValueError(f"Concentration 2 {conc2} out of range [0 .. 100 %]") if not np.isclose((conc1 + conc2), 100, atol=0.1): raise ValueError(f"Conc1 {conc1} and conc2 {conc2} must sum to 100 +- 0.1") - if not (0 <= pressure <= 3): + if not 0 <= pressure <= 3: raise ValueError(f"Pressure {pressure} out of range [0 .. 3 bar abs]") self.gmes.gas1_req.set(gas1).wait(timeout=3) @@ -270,27 +250,13 @@ class IonizationChamber0(PSIDeviceBase): self.gmes.gas2_req.set(gas2).wait(timeout=3) self.gmes.conc2_req.set(conc2).wait(timeout=3) + status = TransitionStatus(self.gmes.status.get(), [0, 1]) + self.cancel_on_stop(status) self.gmes.fill.put(1) - - def wait_for_status(): - return self.gmes.status.get() == 0 - - timeout = 3 - if not self.wait_for_condition(wait_for_status, timeout=timeout, check_stopped=True): - raise TimeoutError( - f"Ionization chamber filling process did not start after {timeout}s. Last log message {self.gmes_status.get()}" - ) - - def wait_for_filling_finished(): - return self.gmes.status.get() == 1 - - # Wait until ionization chamber is filled successfully - status = self.task_handler.submit_task( - task=self.wait_for_condition, task_args=(wait_for_filling_finished, 360, True) - ) if wait: - status.wait() - return status + status.wait(timeout=360) + else: + return status class IonizationChamber1(IonizationChamber0): diff --git a/debye_bec/devices/mo1_bragg/mo1_bragg.py b/debye_bec/devices/mo1_bragg/mo1_bragg.py index 1daec65..7d37e9e 100644 --- a/debye_bec/devices/mo1_bragg/mo1_bragg.py +++ b/debye_bec/devices/mo1_bragg/mo1_bragg.py @@ -56,16 +56,18 @@ class ScanParameter(BaseModel): scan_time: float | None = Field(None, description="Scan time for a half oscillation") scan_duration: float | None = Field(None, description="Duration of the scan") - xrd_enable_low: bool | None = Field( - None, description="XRD enabled for low, should be PV trig_ena_lo_enum" + break_enable_low: bool | None = Field( + None, description="Break enabled for low, should be PV trig_ena_lo_enum" ) # trig_enable_low: bool = None - xrd_enable_high: bool | None = Field( - None, description="XRD enabled for high, should be PV trig_ena_hi_enum" + break_enable_high: bool | None = Field( + None, description="Break enabled for high, should be PV trig_ena_hi_enum" ) # trig_enable_high: bool = None - exp_time_low: float | None = Field(None, description="Exposure time low energy/angle") - exp_time_high: float | None = Field(None, description="Exposure time high energy/angle") + break_time_low: float | None = Field(None, description="Break time low energy/angle") + break_time_high: float | None = Field(None, description="Break time high energy/angle") cycle_low: int | None = Field(None, description="Cycle for low energy/angle") cycle_high: int | None = Field(None, description="Cycle for high energy/angle") + exp_time: float | None = Field(None, description="XRD trigger period") + n_of_trigger: int | None = Field(None, description="Amount of XRD triggers") start: float | None = Field(None, description="Start value for energy/angle") stop: float | None = Field(None, description="Stop value for energy/angle") p_kink: float | None = Field(None, description="P Kink") @@ -140,10 +142,12 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner): self.set_trig_settings( enable_low=False, enable_high=False, - exp_time_low=0, - exp_time_high=0, + break_time_low=0, + break_time_high=0, cycle_low=0, cycle_high=0, + exp_time=0, + n_of_trigger=0, ) self.set_scan_control_settings( mode=ScanControlMode.SIMPLE, scan_duration=self.scan_parameter.scan_duration @@ -155,12 +159,14 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner): scan_time=self.scan_parameter.scan_time, ) self.set_trig_settings( - enable_low=self.scan_parameter.xrd_enable_low, # enable_low=self.scan_parameter.trig_enable_low, - enable_high=self.scan_parameter.xrd_enable_high, # enable_high=self.scan_parameter.trig_enable_high, - exp_time_low=self.scan_parameter.exp_time_low, - exp_time_high=self.scan_parameter.exp_time_high, + enable_low=self.scan_parameter.break_enable_low, + enable_high=self.scan_parameter.break_enable_high, + break_time_low=self.scan_parameter.break_time_low, + break_time_high=self.scan_parameter.break_time_high, cycle_low=self.scan_parameter.cycle_low, cycle_high=self.scan_parameter.cycle_high, + exp_time=self.scan_parameter.exp_time, + n_of_trigger=self.scan_parameter.n_of_trigger, ) self.set_scan_control_settings( mode=ScanControlMode.SIMPLE, scan_duration=self.scan_parameter.scan_duration @@ -176,10 +182,12 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner): self.set_trig_settings( enable_low=False, enable_high=False, - exp_time_low=0, - exp_time_high=0, + break_time_low=0, + break_time_high=0, cycle_low=0, cycle_high=0, + exp_time=0, + n_of_trigger=0, ) self.set_scan_control_settings( mode=ScanControlMode.ADVANCED, scan_duration=self.scan_parameter.scan_duration @@ -193,12 +201,14 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner): e_kink=self.scan_parameter.e_kink, ) self.set_trig_settings( - enable_low=self.scan_parameter.xrd_enable_low, # enable_low=self.scan_parameter.trig_enable_low, - enable_high=self.scan_parameter.xrd_enable_high, # enable_high=self.scan_parameter.trig_enable_high, - exp_time_low=self.scan_parameter.exp_time_low, - exp_time_high=self.scan_parameter.exp_time_high, + enable_low=self.scan_parameter.break_enable_low, + enable_high=self.scan_parameter.break_enable_high, + break_time_low=self.scan_parameter.break_time_low, + break_time_high=self.scan_parameter.break_time_high, cycle_low=self.scan_parameter.cycle_low, cycle_high=self.scan_parameter.cycle_high, + exp_time=self.scan_parameter.exp_time, + n_of_trigger=self.scan_parameter.n_of_trigger, ) self.set_scan_control_settings( mode=ScanControlMode.ADVANCED, scan_duration=self.scan_parameter.scan_duration @@ -213,7 +223,7 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner): status = CompareStatus(self.scan_control.scan_msg, ScanControlLoadMessage.SUCCESS) self.cancel_on_stop(status) self.scan_control.scan_load.put(1) - # Wait for params to be checked from controller + # Wait for params to be checked from controller status.wait(self.timeout_for_pvwait) return None @@ -311,7 +321,7 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner): status_list.append(self.scan_settings.s_scan_scantime.set(scan_time)) self.cancel_on_stop(status_list[-1]) - + for s in status_list: s.wait(timeout=self.timeout_for_pvwait) @@ -342,7 +352,7 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner): status = CompareStatus(self.calculator.calc_done, 1) self.cancel_on_stop(status) status.wait(self.timeout_for_pvwait) - time.sleep(0.25) #TODO needed still? Needed due to update frequency of softIOC + time.sleep(0.25) # TODO needed still? Needed due to update frequency of softIOC if mode == "AngleToEnergy": return self.calculator.calc_energy.get() elif mode == "EnergyToAngle": @@ -391,22 +401,24 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner): self, enable_low: bool, enable_high: bool, - exp_time_low: int, - exp_time_high: int, + break_time_low: float, + break_time_high: float, cycle_low: int, cycle_high: int, + exp_time: float, + n_of_trigger: int, ) -> None: """Set TRIG settings for the upcoming scan. Args: enable_low (bool): Enable TRIG for low energy/angle enable_high (bool): Enable TRIG for high energy/angle - num_trigger_low (int): Number of triggers for low energy/angle - num_trigger_high (int): Number of triggers for high energy/angle - exp_time_low (int): Exposure time for low energy/angle - exp_time_high (int): Exposure time for high energy/angle + break_time_low (float): Exposure time for low energy/angle + break_time_high (float): Exposure time for high energy/angle cycle_low (int): Cycle for low energy/angle cycle_high (int): Cycle for high energy/angle + exp_time (float): Length of 1 trigger period in seconds + n_of_trigger (int): Amount of triggers to be fired during brake """ status_list = [] @@ -417,10 +429,10 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner): status_list.append(self.scan_settings.trig_ena_lo_enum.set(int(enable_low))) self.cancel_on_stop(status_list[-1]) - status_list.append(self.scan_settings.trig_time_hi.set(exp_time_high)) + status_list.append(self.scan_settings.trig_time_hi.set(break_time_high)) self.cancel_on_stop(status_list[-1]) - status_list.append(self.scan_settings.trig_time_lo.set(exp_time_low)) + status_list.append(self.scan_settings.trig_time_lo.set(break_time_low)) self.cancel_on_stop(status_list[-1]) status_list.append(self.scan_settings.trig_every_n_hi.set(cycle_high)) @@ -429,10 +441,15 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner): status_list.append(self.scan_settings.trig_every_n_lo.set(cycle_low)) self.cancel_on_stop(status_list[-1]) + status_list.append(self.trigger_settings.xrd_trig_period.set(exp_time)) + self.cancel_on_stop(status_list[-1]) + + status_list.append(self.trigger_settings.xrd_n_of_trig.set(n_of_trigger)) + self.cancel_on_stop(status_list[-1]) + for s in status_list: s.wait(timeout=self.timeout_for_pvwait) - def set_scan_control_settings(self, mode: ScanControlMode, scan_duration: float) -> None: """Set the scan control settings for the upcoming scan. @@ -453,7 +470,6 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner): for s in status_list: s.wait(timeout=self.timeout_for_pvwait) - def _update_scan_parameter(self): """Get the scan_info parameters for the scan.""" for key, value in self.scan_info.msg.request_inputs["inputs"].items(): diff --git a/debye_bec/devices/mo1_bragg/mo1_bragg_devices.py b/debye_bec/devices/mo1_bragg/mo1_bragg_devices.py index 0977c17..6a4fe1a 100644 --- a/debye_bec/devices/mo1_bragg/mo1_bragg_devices.py +++ b/debye_bec/devices/mo1_bragg/mo1_bragg_devices.py @@ -139,21 +139,29 @@ class Mo1TriggerSettings(Device): xrd_trig_src_enum = Cpt(EpicsSignalWithRBV, suffix="xrd_trig_src_ENUM", kind="config") xrd_trig_mode_enum = Cpt(EpicsSignalWithRBV, suffix="xrd_trig_mode_ENUM", kind="config") xrd_trig_len = Cpt(EpicsSignalWithRBV, suffix="xrd_trig_len", kind="config") + xrd_trig_period = Cpt(EpicsSignalWithRBV, suffix="xrd_trig_period", kind="config") + xrd_n_of_trig = Cpt(EpicsSignalWithRBV, suffix="xrd_n_of_trig", kind="config") xrd_trig_req = Cpt(EpicsSignal, suffix="xrd_trig_req", kind="config") falcon_trig_src_enum = Cpt(EpicsSignalWithRBV, suffix="falcon_trig_src_ENUM", kind="config") falcon_trig_mode_enum = Cpt(EpicsSignalWithRBV, suffix="falcon_trig_mode_ENUM", kind="config") falcon_trig_len = Cpt(EpicsSignalWithRBV, suffix="falcon_trig_len", kind="config") + falcon_trig_period = Cpt(EpicsSignalWithRBV, suffix="falcon_trig_period", kind="config") + falcon_n_of_trig = Cpt(EpicsSignalWithRBV, suffix="falcon_n_of_trig", kind="config") falcon_trig_req = Cpt(EpicsSignal, suffix="falcon_trig_req", kind="config") univ1_trig_src_enum = Cpt(EpicsSignalWithRBV, suffix="univ1_trig_src_ENUM", kind="config") univ1_trig_mode_enum = Cpt(EpicsSignalWithRBV, suffix="univ1_trig_mode_ENUM", kind="config") univ1_trig_len = Cpt(EpicsSignalWithRBV, suffix="univ1_trig_len", kind="config") + univ1_trig_period = Cpt(EpicsSignalWithRBV, suffix="univ1_trig_period", kind="config") + univ1_n_of_trig = Cpt(EpicsSignalWithRBV, suffix="univ1_n_of_trig", kind="config") univ1_trig_req = Cpt(EpicsSignal, suffix="univ1_trig_req", kind="config") univ2_trig_src_enum = Cpt(EpicsSignalWithRBV, suffix="univ2_trig_src_ENUM", kind="config") univ2_trig_mode_enum = Cpt(EpicsSignalWithRBV, suffix="univ2_trig_mode_ENUM", kind="config") univ2_trig_len = Cpt(EpicsSignalWithRBV, suffix="univ2_trig_len", kind="config") + univ2_trig_period = Cpt(EpicsSignalWithRBV, suffix="univ2_trig_period", kind="config") + univ2_n_of_trig = Cpt(EpicsSignalWithRBV, suffix="univ2_n_of_trig", kind="config") univ2_trig_req = Cpt(EpicsSignal, suffix="univ2_trig_req", kind="config") diff --git a/debye_bec/devices/nidaq/nidaq.py b/debye_bec/devices/nidaq/nidaq.py index 2942559..3b7bb93 100644 --- a/debye_bec/devices/nidaq/nidaq.py +++ b/debye_bec/devices/nidaq/nidaq.py @@ -302,6 +302,12 @@ class NidaqControl(Device): SetableSignal, value=0, kind=Kind.normal, doc="NIDAQ stream counter input 7. STD" ) + xas_timestamp = Cpt(SetableSignal, value=0, kind=Kind.normal, doc="NIDAQ stream XAS timestamp") + + xrd_timestamp = Cpt(SetableSignal, value=0, kind=Kind.normal, doc="NIDAQ stream XRD timestamp") + + xrd_energy = Cpt(SetableSignal, value=0, kind=Kind.normal, doc="NIDAQ stream XRD energy") + di0_max = Cpt(SetableSignal, value=0, kind=Kind.normal, doc="NIDAQ stream digital input 0, MAX") di1_max = Cpt(SetableSignal, value=0, kind=Kind.normal, doc="NIDAQ stream digital input 1, MAX") di2_max = Cpt(SetableSignal, value=0, kind=Kind.normal, doc="NIDAQ stream digital input 2, MAX") diff --git a/debye_bec/devices/pilatus/__init__.py b/debye_bec/devices/pilatus/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/debye_bec/devices/pilatus/pilatus.py b/debye_bec/devices/pilatus/pilatus.py new file mode 100644 index 0000000..e12aac1 --- /dev/null +++ b/debye_bec/devices/pilatus/pilatus.py @@ -0,0 +1,701 @@ +"""Pilatus AD integration at Debye beamline.""" + +from __future__ import annotations + +import enum +import threading +import time +import traceback +from typing import TYPE_CHECKING + +import numpy as np +from bec_lib.file_utils import get_full_path +from bec_lib.logger import bec_logger +from ophyd import Component as Cpt +from ophyd import EpicsSignal, Kind +from ophyd.areadetector.cam import ADBase, PilatusDetectorCam +from ophyd.areadetector.plugins import HDF5Plugin_V22 as HDF5Plugin +from ophyd.areadetector.plugins import ImagePlugin_V22 as ImagePlugin +from ophyd.status import WaitTimeoutError +from ophyd_devices import CompareStatus, DeviceStatus, FileEventSignal, PreviewSignal +from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase +from pydantic import BaseModel, Field + +from debye_bec.devices.pilatus.utils import AndStatusWithList + +if TYPE_CHECKING: # pragma: no cover + from bec_lib.devicemanager import ScanInfo + from bec_lib.messages import DevicePreviewMessage, ScanStatusMessage + from bec_server.device_server.device_server import DeviceManagerDS + +PILATUS_READOUT_TIME = 0.1 # in s +# PILATUS_ACQUIRE_TIME = ( +# 999999 # This time is the timeout of the detector in operation mode, so it needs to be large. +# ) + +# pylint: disable=redefined-outer-name +# pylint: disable=raise-missing-from + +logger = bec_logger.logger + + +class DETECTORSTATE(int, enum.Enum): + """Pilatus Detector States from CamServer""" + + UNARMED = 0 + ARMED = 1 + + +class ACQUIREMODE(int, enum.Enum): + """Pilatus Acquisition Modes""" + + DONE = 0 + ACQUIRING = 1 + + +class FILEWRITEMODE(int, enum.Enum): + """HDF5 Plugin FileWrite Mode""" + + SINGLE = 0 + CAPTURE = 1 + STREAM = 2 + + +class COMPRESSIONALGORITHM(int, enum.Enum): + """HDF5 Plugin Compression Algorithm""" + + NONE = 0 + NBIT = 1 # Don't use that.. + SZIP = 2 + ZLIB = 3 + + +class TRIGGERMODE(int, enum.Enum): + """Pilatus Trigger Modes""" + + INTERNAL = 0 + EXT_ENABLE = 1 + EXT_TRIGGER = 2 + MULT_TRIGGER = 3 + ALIGNMENT = 4 + + +class MONOTRIGGERSOURCE(int, enum.Enum): + """ "Mono XRD trigger source""" + + EPICS = 0 + INPOS = 1 + + +class MONOTRIGGERMODE(int, enum.Enum): + """ "Mono XRD trigger mode""" + + PULSE = 0 + CONDITION = 1 + + def description(self) -> str: + """Return a description of the trigger mode.""" + descriptions = { + TRIGGERMODE.INTERNAL: "Internal trigger mode, images are acquired on internal trigger.", + TRIGGERMODE.EXT_ENABLE: "External Enable trigger mode; check manual as details are currently unknown", + TRIGGERMODE.EXT_TRIGGER: "External Trigger mode, images are acquired on external trigger signal. All images on single trigger.", + TRIGGERMODE.MULT_TRIGGER: "Multiple External Trigger mode, images are acquired on multiple external trigger signals. One image per trigger.", + TRIGGERMODE.ALIGNMENT: "Alignment mode, used for beam alignment.", + } + return descriptions.get(self, "Unknown") + + def __str__(self): + return self.description() + + +class ScanParameter(BaseModel): + """Dataclass to store the scan parameters for the Pilatus. + This needs to be in sync with the kwargs of the XRD related scans from Debye, to + ensure that the scan parameters are correctly set. Any changes in the scan kwargs, + i.e. renaming or adding new parameters, need to be represented here as well.""" + + scan_time: float | None = Field(None, description="Scan time for a half oscillation") + scan_duration: float | None = Field(None, description="Duration of the scan") + break_enable_low: bool | None = Field( + None, description="Break enabled for low, should be PV trig_ena_lo_enum" + ) # trig_enable_low: bool = None + break_enable_high: bool | None = Field( + None, description="Break enabled for high, should be PV trig_ena_hi_enum" + ) # trig_enable_high: bool = None + break_time_low: float | None = Field(None, description="Break time low energy/angle") + break_time_high: float | None = Field(None, description="Break time high energy/angle") + cycle_low: int | None = Field(None, description="Cycle for low energy/angle") + cycle_high: int | None = Field(None, description="Cycle for high energy/angle") + exp_time: float | None = Field(None, description="XRD trigger period") + n_of_trigger: int | None = Field(None, description="Amount of XRD triggers") + start: float | None = Field(None, description="Start value for energy/angle") + stop: float | None = Field(None, description="Stop value for energy/angle") + model_config: dict = {"validate_assignment": True} + + +class Pilatus(PSIDeviceBase, ADBase): + """ + Pilatus Base integration for Debye. + Prefix of the detector is 'X01DA-ES2-PIL:' + + Args: + prefix (str) : Prefix for the IOC + name (str) : Name of the detector + scan_info (ScanInfo | None) : ScanInfo object passed through the device by the device_manager + device_manager (DeviceManager | None) : DeviceManager object passed through the device by the device_manager + """ + + # USER_ACCESS = ["start_live_mode", "stop_live_mode"] + + cam = Cpt(PilatusDetectorCam, "cam1:") + hdf = Cpt(HDF5Plugin, "HDF1:") + image1 = Cpt(ImagePlugin, "image1:") + filter_number = Cpt( + EpicsSignal, "cam1:FileNumber", kind=Kind.omitted, doc="File number for ramdisk" + ) + trigger_shot = Cpt( + EpicsSignal, + read_pv="X01DA-OP-MO1:BRAGG:xrd_trig_req", + write_pv="X01DA-OP-MO1:BRAGG:xrd_trig_req", + add_prefix=("a",), + kind=Kind.omitted, + doc="Trigger PV from MO1 Bragg", + ) + trigger_source = Cpt( + EpicsSignal, + read_pv="X01DA-OP-MO1:BRAGG:xrd_trig_src_ENUM_RBV", + write_pv="X01DA-OP-MO1:BRAGG:xrd_trig_src_ENUM", + add_prefix=("a",), + kind=Kind.omitted, + doc="Trigger Source; PV, 0 : EPICS, 1 : INPOS", + ) + trigger_mode = Cpt( + EpicsSignal, + read_pv="X01DA-OP-MO1:BRAGG:xrd_trig_mode_ENUM_RBV", + write_pv="X01DA-OP-MO1:BRAGG:xrd_trig_mode_ENUM", + add_prefix=("a",), + kind=Kind.omitted, + doc="Trigger Mode; 0 : PULSE, 1 : CONDITION", + ) + trigger_pulse_length = Cpt( + EpicsSignal, + read_pv="X01DA-OP-MO1:BRAGG:xrd_trig_len_RBV", + write_pv="X01DA-OP-MO1:BRAGG:xrd_trig_len", + add_prefix=("a",), + kind=Kind.omitted, + doc="Trigger Period in seconds", + ) + trigger_period = Cpt( + EpicsSignal, + read_pv="X01DA-OP-MO1:BRAGG:xrd_trig_period_RBV", + write_pv="X01DA-OP-MO1:BRAGG:xrd_trig_period", + add_prefix=("a",), + kind=Kind.omitted, + doc="Trigger Pulse Length in seconds", + ) + trigger_n_of = Cpt( + EpicsSignal, + read_pv="X01DA-OP-MO1:BRAGG:xrd_n_of_trig_RBV", + write_pv="X01DA-OP-MO1:BRAGG:xrd_n_of_trig", + add_prefix=("a",), + kind=Kind.omitted, + doc="Number of trigger to generate for each request", + ) + preview = Cpt( + PreviewSignal, + name="preview", + ndim=2, + num_rotation_90=0, # Check this + doc="Preview signal for the Pilatus Detector", + ) + file_event = Cpt(FileEventSignal, name="file_event") + + @property + def baseline_signals(self): + """Define baseline signals""" + return [ + self.cam.acquire_time, + self.cam.num_exposures, + self.cam.threshold_energy, + self.cam.gain_menu, + self.cam.pixel_cut_off, + ] + + def __init__( + self, + *, + name: str, + prefix: str = "", + scan_info: ScanInfo | None = None, + device_manager: DeviceManagerDS | None = None, + **kwargs, + ): + super().__init__( + name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs + ) + self.scan_parameter = ScanParameter() + self.device_manager = device_manager + self._readout_time = PILATUS_READOUT_TIME + self._full_path = "" + self._poll_thread = threading.Thread( + target=self._poll_array_data, daemon=True, name=f"{self.name}_poll_thread" + ) + self._poll_thread_kill_event = threading.Event() + self._poll_rate = 1 # Poll rate in Hz + self.xas_xrd_scan_names = ["xas_simple_scan_with_xrd", "xas_advanced_scan_with_xrd"] + self.n_images = None + # self._live_mode_thread = threading.Thread( + # target=self._live_mode_loop, daemon=True, name=f"{self.name}_live_mode_thread" + # ) + # self._live_mode_kill_event = threading.Event() + # self._live_mode_run_event = threading.Event() + # self._live_mode_stopped_event = threading.Event() + # self._live_mode_stopped_event.set() # Initial state is stopped + + ######################################## + # Custom Beamline Methods # + ######################################## + + def _poll_array_data(self): + """Poll the array data for preview updates.""" + while not self._poll_thread_kill_event.wait(1 / self._poll_rate): + try: + # logger.info(f"Running poll loop for {self.name}..") + value = self.image1.array_data.get() + if value is None: + continue + width = self.image1.array_size.width.get() + height = self.image1.array_size.height.get() + # Geometry correction for the image + data = np.reshape(value, (height, width)) + last_image: DevicePreviewMessage = self.preview.get() + # logger.info(f"Preview image for {self.name} has shape {data.shape}") + if last_image is not None: + if np.array_equal(data, last_image.data): + # No update if image is the same, ~2.5ms on 2400x2400 image (6M) + logger.debug( + f"Pilatus preview image for {self.name} is the same as last one, not updating." + ) + continue + + logger.debug(f"Setting preview data for {self.name}") + self.preview.put(data) + except Exception: # pylint: disable=broad-except + content = traceback.format_exc() + logger.error( + f"Error while polling array data for preview of {self.name}: {content}" + ) + + # def start_live_mode(self, exp_time: float, n_images_max: int = 50000): + # """ + # Start live mode with given exposure time. + + # Args: + # exp_time (float) : Exposure time in seconds + # n_images_max (int): Maximum number of images to capture during live mode. + # Default is 5000. Only reset if needed. + # """ + # if ( + # self.cam.acquire.get() != ACQUIREMODE.DONE.value + # or self.hdf.capture.get() != ACQUIREMODE.DONE.value + # ): + # logger.warning(f"Can't start live mode, acquisition running on detector {self.name}.") + # return + # if self._live_mode_run_event.is_set(): + # logger.warning(f"Live mode is already running on detector {self.name}.") + # return + + # # Set relevant PVs + # self.cam.array_counter.set(0).wait(5) # Reset array counter + # self.cam.num_images.set(n_images_max).wait(5) + # logger.info( + # f"Setting exposure time to {exp_time} s for live mode on {self.name} with {n_images_max} images." + # ) + # self.cam.acquire_time.set(exp_time - self._readout_time).wait(5) + # self.cam.acquire_period.set(exp_time).wait(5) + + # status = CompareStatus(self.cam.acquire, ACQUIREMODE.DONE.value) + # # It should suffice to make sure that self.hdf.capture is not set.. + # self.cam.acquire.put(1) # Start measurement + # try: + # status.wait(10) + # except WaitTimeoutError: + # content = traceback.format_exc() + # raise RuntimeError( + # f"Live Mode on detector {self.name} did not stop: {content} after 10s." + # ) + # self._live_mode_run_event.set() + + # def _live_mode_loop(self, exp_time: float): + # while not self._live_mode_kill_event.is_set(): + # self._live_mode_run_event.wait() + # self._live_mode_stopped_event.clear() # Clear stopped event + # time.sleep(self._readout_time) # make sure to wait for the readout_time + # n_images = self.cam.array_counter.get() + # status = CompareStatus(self.cam.array_counter, n_images + 1) + # self.trigger_shot.put(1) + # try: + # status.wait(60) + # except WaitTimeoutError: + # logger.warning( + # f"Live mode timeout exceeded for {self.name}. Continuing in live_mode_loop" + # ) + # if self._live_mode_run_event.is_set(): + # self._live_mode_stopped_event.set() # Set stopped event to indicate that live mode loop is stopped + + # def stop_live_mode(self): + # """Stop live mode.""" + # if self._live_mode_stopped_event.is_set(): + # return + # status = CompareStatus(self.cam.acquire, ACQUIREMODE.DONE.value) + # self.cam.acquire.put(0) + # self._live_mode_run_event.clear() + # if not self._live_mode_stopped_event.wait(10): # Wait until live mode loop is stopped + # logger.warning(f"Live mode did not stop in time for {self.name}.") + # try: + # status.wait(10) + # except WaitTimeoutError: + # content = traceback.format_exc() + # raise RuntimeError( + # f"Live Mode on detector {self.name} did not stop: {content} after 10s." + # ) + + def check_detector_stop_running_acquisition(self) -> AndStatusWithList: + """Check if the detector is still running an acquisition.""" + status_acquire = CompareStatus(self.cam.acquire, ACQUIREMODE.DONE.value) + status_writing = CompareStatus(self.hdf.capture, ACQUIREMODE.DONE.value) + status_cam_server = CompareStatus(self.cam.armed, DETECTORSTATE.UNARMED.value) + status = AndStatusWithList( + device=self, status_list=[status_acquire, status_writing, status_cam_server] + ) + return status + + def _calculate_trigger(self, scan_msg: ScanStatusMessage): + self._update_scan_parameter() + total_osc = 0 + total_trig_lo = 0 + total_trig_hi = 0 + calc_duration = 0 + n_trig_lo = 1 + n_trig_hi = 1 + init_lo = 1 + init_hi = 1 + lo_done = 0 + hi_done = 0 + if not self.scan_parameter.break_enable_low: + lo_done = 1 + if not self.scan_parameter.break_enable_high: + hi_done = 1 + start_time = time.time() + while True: + # TODO, we should not use infinite loops, for now let's add the escape Timeout of 20s, but should eventually be reviewed. + if time.time() - start_time > 20: + raise RuntimeError( + f"Calculating the number of triggers for scan {scan_msg.scan_name} took more than 20 seconds, aborting." + ) + total_osc = total_osc + 2 + calc_duration = calc_duration + 2 * self.scan_parameter.scan_time + + if self.scan_parameter.break_enable_low and n_trig_lo >= self.scan_parameter.cycle_low: + n_trig_lo = 1 + calc_duration = calc_duration + self.scan_parameter.break_time_low + if init_lo: + lo_done = 1 + init_lo = 0 + else: + n_trig_lo += 1 + + if ( + self.scan_parameter.break_enable_high + and n_trig_hi >= self.scan_parameter.cycle_high + ): + n_trig_hi = 1 + calc_duration = calc_duration + self.scan_parameter.break_time_high + if init_hi: + hi_done = 1 + init_hi = 0 + else: + n_trig_hi += 1 + + if lo_done and hi_done: + n = np.floor(self.scan_parameter.scan_duration / calc_duration) + total_osc = total_osc * n + if self.scan_parameter.break_enable_low: + total_trig_lo = n + 1 + if self.scan_parameter.break_enable_high: + total_trig_hi = n + 1 + calc_duration = calc_duration * n + lo_done = 0 + hi_done = 0 + + if calc_duration >= self.scan_parameter.scan_duration: + break + + return total_trig_lo + total_trig_hi + + ######################################## + # Beamline Specific Implementations # + ######################################## + + def on_init(self) -> None: + """ + Called when the device is initialized. + + No signals are connected at this point. If you like to + set default values on signals, please use on_connected instead. + """ + + def on_connected(self) -> None: + """ + Called after the device is connected and its signals are connected. + Default values for signals should be set here. + """ + + status_cam = CompareStatus(self.cam.acquire, ACQUIREMODE.DONE.value) + status_hdf = CompareStatus(self.hdf.capture, ACQUIREMODE.DONE.value) + try: + status_cam.wait(timeout=5) + status_hdf.wait(timeout=5) + except WaitTimeoutError: + logger.warning( + f"Camera device {self.name} was running an acquisition. Stopping acquisition." + ) + self.cam.acquire.put(0) + self.hdf.capture.put(0) + + self.cam.trigger_mode.set(TRIGGERMODE.MULT_TRIGGER.value).wait(5) + self.cam.image_file_tmot.set(60).wait(5) + self.hdf.file_write_mode.set(FILEWRITEMODE.STREAM.value).wait(5) + self.hdf.file_template.set("%s%s").wait(5) + self.hdf.auto_save.set(1).wait(5) + self.hdf.lazy_open.set(1).wait(5) + self.hdf.compression.set(COMPRESSIONALGORITHM.NONE.value).wait(5) # To test which to use + # Start polling thread... + self._poll_thread.start() + # Start live mode thread... + # self._live_mode_thread.start() + + def on_stage(self) -> DeviceStatus | None: + """ + Called while staging the device. + + Information about the upcoming scan can be accessed from the scan_info + (self.scan_info.msg) object. + """ + # self.stop_live_mode() # Make sure that live mode is stopped if scan runs + + scan_msg: ScanStatusMessage = self.scan_info.msg + if scan_msg.scan_name in self.xas_xrd_scan_names: + self._update_scan_parameter() + # Compute number of triggers + total_trig_lo, total_trig_hi = self._calculate_trigger(scan_msg) + # Set the number of images, we may also set this to a higher values if preferred and stop the acquisition + # TODO This logic is prone to errors, as we rely on the scans to nicely resolve to n_images. We should + # use here instead a way of settings the n_images independently of the scan parameters to avoid running out of sync + # with the complete method. Ideally we comput them in the scan itself.. This is much safer IMO! + self.n_images = (total_trig_lo + total_trig_hi) * self.scan_parameter.n_of_trigger + exp_time = self.scan_parameter.exp_time + self.trigger_source.set(MONOTRIGGERSOURCE.INPOS).wait(5) + self.trigger_n_of.set(self.scan_parameter.n_of_trigger).wait(5) + + elif scan_msg.scan_type == "step": + self.n_images = scan_msg.num_points * scan_msg.scan_parameters.get( + "frames_per_trigger", 1 + ) + exp_time = scan_msg.scan_parameters.get("exp_time") + self.trigger_source.set(MONOTRIGGERSOURCE.EPICS).wait(5) + self.trigger_n_of.set(1).wait(5) # BEC will trigger each acquisition + else: + # TODO how to deal with fly scans? + return None + # Common settings + self.trigger_mode.set(MONOTRIGGERMODE.PULSE).wait(5) + self.trigger_period.set(exp_time).wait(5) + self.trigger_pulse_length.set(0.005).wait( + 5 + ) # Pulse length of 5 ms enough for Pilatus and NIDAQ + + if exp_time - self._readout_time <= 0: + raise ValueError( + ( + f"Exposure time {exp_time} is too short ", + f"for Pilatus with readout_time {self._readout_time}.", + ) + ) + detector_exp_time = exp_time - self._readout_time + self._full_path = get_full_path(scan_msg, name="pilatus") + file_path = "/".join(self._full_path.split("/")[:-1]) + file_name = self._full_path.split("/")[-1] + # Prepare detector and backend + self.cam.array_callbacks.set(1).wait(5) # Enable array callbacks + self.hdf.enable.set(1).wait(5) # Enable HDF5 plugin + # Camera settings + self.cam.num_exposures.set(1).wait(5) + self.cam.num_images.set(self.n_images).wait(5) + self.cam.acquire_time.set(detector_exp_time).wait(5) # let's try this + self.cam.acquire_period.set(exp_time).wait(5) + self.filter_number.set(0).wait(5) + # HDF5 settings + logger.debug( + f"Setting HDF5 file path to {file_path} and file name to {file_name}. full_path is {self._full_path}" + ) + self.hdf.file_path.set(file_path).wait(5) + self.hdf.file_name.set(file_name).wait(5) + self.hdf.num_capture.set(self.n_images).wait(5) + self.cam.array_counter.set(0).wait(5) # Reset array counter + self.file_event.put( + file_path=self._full_path, + done=False, + successful=False, + hinted_h5_entries={"data": "/entry/data/data"}, + ) + + def on_unstage(self) -> None: + """Called while unstaging the device.""" + + def on_pre_scan(self) -> DeviceStatus | None: + """Called right before the scan starts on all devices automatically.""" + scan_msg: ScanStatusMessage = self.scan_info.msg + if ( + scan_msg.scan_name in self.xas_xrd_scan_names or scan_msg.scan_type == "step" + ): # TODO how to deal with fly scans? + status_hdf = CompareStatus(self.hdf.capture, ACQUIREMODE.ACQUIRING.value) + status_cam = CompareStatus(self.cam.acquire, ACQUIREMODE.ACQUIRING.value) + status_cam_server = CompareStatus(self.cam.armed, DETECTORSTATE.ARMED.value) + status = AndStatusWithList( + device=self, status_list=[status_hdf, status_cam, status_cam_server] + ) + self.cam.acquire.put(1) + self.hdf.capture.put(1) + self.cancel_on_stop(status) + return status + else: + return None + + def on_trigger(self) -> DeviceStatus | None: + """Called when the device is triggered.""" + scan_msg: ScanStatusMessage = self.scan_info.msg + if not scan_msg.scan_type == "step": + return None + start_time = time.time() + img_counter = self.hdf.num_captured.get() + logger.debug(f"Triggering image with num_captured {img_counter}") + status = CompareStatus(self.hdf.num_captured, img_counter + 1) + logger.debug(f"Triggering took image {time.time() - start_time:.3f} seconds") + self.trigger_shot.put(1) + self.cancel_on_stop(status) + return status + + def _complete_callback(self, status: DeviceStatus): + """Callback for when the device completes a scan.""" + scan_msg: ScanStatusMessage = self.scan_info.msg + if ( + scan_msg.scan_name in self.xas_xrd_scan_names or scan_msg.scan_type == "step" + ): # TODO how to deal with fly scans? + if status.success: + status.device.file_event.put( + file_path=status.device._full_path, # pylint: disable:protected-access + done=True, + successful=True, + hinted_h5_entries={"data": "/entry/data/data"}, + ) + else: + status.device.file_event.put( + file_path=status.device._full_path, # pylint: disable:protected-access + done=True, + successful=False, + hinted_h5_entries={"data": "/entry/data/data"}, + ) + else: + return None + + def on_complete(self) -> DeviceStatus | None: + """Called to inquire if a device has completed a scans.""" + scan_msg: ScanStatusMessage = self.scan_info.msg + if ( + scan_msg.scan_name in self.xas_xrd_scan_names or scan_msg.scan_type == "step" + ): # TODO how to deal with fly scans? + status_hdf = CompareStatus(self.hdf.capture, ACQUIREMODE.DONE.value) + status_cam = CompareStatus(self.cam.acquire, ACQUIREMODE.DONE.value) + status_cam_server = CompareStatus(self.cam.armed, DETECTORSTATE.UNARMED.value) + if self.scan_info.msg.scan_name in self.xas_xrd_scan_names: + # For long scans, it can be that the mono will execute one cycle more, + # meaning a few more XRD triggers will be sent + status_img_written = CompareStatus( + self.hdf.num_captured, self.n_images, operation=">=" + ) + else: + status_img_written = CompareStatus(self.hdf.num_captured, self.n_images) + status_img_written = CompareStatus(self.hdf.num_captured, self.n_images) + status = AndStatusWithList( + device=self, + status_list=[status_hdf, status_cam, status_img_written, status_cam_server], + ) + status.add_callback(self._complete_callback) # Callback that writing was successful + self.cancel_on_stop(status) + return status + else: + return None + + def on_kickoff(self) -> None: + """Called to kickoff a device for a fly scan. Has to be called explicitly.""" + + def on_stop(self) -> None: + """Called when the device is stopped.""" + self.cam.acquire.put(0) + self.hdf.capture.put(0) + + def on_destroy(self) -> None: + """Called when the device is destroyed. Cleanup resources here.""" + self._poll_thread_kill_event.set() + # TODO do we need to clean the poll thread ourselves? + self.on_stop() + + def _update_scan_parameter(self): + """Get the scan_info parameters for the scan.""" + for key, value in self.scan_info.msg.request_inputs["inputs"].items(): + if hasattr(self.scan_parameter, key): + setattr(self.scan_parameter, key, value) + for key, value in self.scan_info.msg.request_inputs["kwargs"].items(): + if hasattr(self.scan_parameter, key): + setattr(self.scan_parameter, key, value) + + +if __name__ == "__main__": + try: + pilatus = Pilatus(name="pilatus", prefix="X01DA-ES2-PIL:") + logger.info("Calling wait for connection") + # pilatus.wait_for_connection(all_signals=True, timeout=20) + logger.info("Connecting to pilatus...") + pilatus.on_connected() + for exp_time, scan_number, n_pnts in zip([0.5, 1.0, 2.0], [1, 2, 3], [30, 20, 10]): + logger.info("Sleeping for 5s") + time.sleep(5) + pilatus.scan_info.msg.num_points = n_pnts + pilatus.scan_info.msg.scan_parameters["exp_time"] = exp_time + pilatus.scan_info.msg.scan_parameters["frames_per_trigger"] = 1 + pilatus.scan_info.msg.info["file_components"] = ( + f"/sls/x01da/data/p22481/raw/data/S00000-00999/S{scan_number:05d}/S{scan_number:05d}", + "h5", + ) + pilatus.on_stage() + logger.info("Stage done") + pilatus.on_pre_scan().wait(timeout=5) + logger.info("Pre-scan done") + for ii in range(pilatus.scan_info.msg.num_points): + # if ii == 0: + # time.sleep(1) + logger.info(f"Triggering image {ii+1}/{pilatus.scan_info.msg.num_points}") + pilatus.on_trigger().wait() + p = pilatus.preview.get() + if p is not None: + p: DevicePreviewMessage + logger.warning( + f"Preview shape: {p.data.shape}, max: {np.max(p.data)}, min: {np.min(p.data)}, mean: {np.mean(p.data)}" + ) + pilatus.on_complete().wait(timeout=5) + logger.info("Complete done") + pilatus.on_unstage() + logger.info("Unstage done") + finally: + pilatus.on_destroy() diff --git a/debye_bec/devices/pilatus/utils.py b/debye_bec/devices/pilatus/utils.py new file mode 100644 index 0000000..d3f3de3 --- /dev/null +++ b/debye_bec/devices/pilatus/utils.py @@ -0,0 +1,238 @@ +"""Temporary utility module for Status Object implementations.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from ophyd import Device, DeviceStatus, StatusBase + + +class AndStatusWithList(DeviceStatus): + """ + Custom implementation of the AndStatus that combines the + option to add multiple statuses as a list, and in addition + allows for adding the Device as an object to access its + methods. + + Args""" + + def __init__( + self, + device: Device, + status_list: StatusBase | DeviceStatus | list[StatusBase | DeviceStatus], + **kwargs, + ): + self.all_statuses = status_list if isinstance(status_list, list) else [status_list] + super().__init__(device=device, **kwargs) + self._trace_attributes["all"] = [st._trace_attributes for st in self.all_statuses] + + def inner(status): + with self._lock: + if self._externally_initiated_completion: + return + if self.done: # Return if status is already done.. It must be resolved already + return + + for st in self.all_statuses: + with st._lock: + if st.done and not st.success: + self.set_exception(st.exception()) # st._exception + return + + if all(st.done for st in self.all_statuses) and all( + st.success for st in self.all_statuses + ): + self.set_finished() + + for st in self.all_statuses: + with st._lock: + st.add_callback(inner) + + # TODO improve __repr__ and __str__ + def __repr__(self): + return "".format(self=self) + + def __str__(self): + return "".format(self=self) + + def __contains__(self, status: StatusBase | DeviceStatus) -> bool: + for child in self.all_statuses: + if child == status: + return True + if isinstance(child, AndStatusWithList): + if status in child: + return True + + return False + + # TODO Check if this actually works.... + def set_exception(self, exc): + super().set_exception(exc) + # Propagate the exception to all sub-statuses that are not done yet. + with self._lock: + for st in self.all_statuses: + with st._lock: + if not st.done: + st.set_exception(exc) + + def _run_callbacks(self): + """ + Set the Event and run the callbacks. + """ + if self.timeout is None: + timeout = None + else: + timeout = self.timeout + self.settle_time + if not self._settled_event.wait(timeout): + self.log.warning("%r has timed out", self) + with self._externally_initiated_completion_lock: + if self._exception is None: + exc = TimeoutError( + f"AndStatus from device {self.device.name} failed to complete in specified timeout of {self.timeout + self.settle_time}." + ) + self._exception = exc + # Mark this as "settled". + try: + self._settled() + except Exception: + self.log.exception("%r encountered error during _settled()", self) + with self._lock: + self._event.set() + if self._exception is not None: + try: + self._handle_failure() + except Exception: + self.log.exception("%r encountered an error during _handle_failure()", self) + for cb in self._callbacks: + try: + cb(self) + except Exception: + self.log.exception( + "An error was raised on a background thread while " + "running the callback %r(%r).", + cb, + self, + ) + self._callbacks.clear() + + +class AndStatus(StatusBase): + """Custom AndStatus for TimePix detector.""" + + def __init__( + self, + left: StatusBase | DeviceStatus | list[StatusBase | DeviceStatus] | None, + name: str | Device | None = None, + right: StatusBase | DeviceStatus | list[StatusBase | DeviceStatus] | None = None, + **kwargs, + ): + self.left = left if isinstance(left, list) else [left] + if right is not None: + self.right = right if isinstance(right, list) else [right] + else: + self.right = [] + self.all_statuses = self.left + self.right + if name is None: + name = "unname_status" + elif isinstance(name, Device): + name = name.name + else: + name = name + self.name = name + super().__init__(**kwargs) + self._trace_attributes["left"] = [st._trace_attributes for st in self.left] + self._trace_attributes["right"] = [st._trace_attributes for st in self.right] + + def inner(status): + with self._lock: + if self._externally_initiated_completion: + return + if self.done: # Return if status is already done.. It must be resolved already + return + + for st in self.all_statuses: + with st._lock: + if st.done and not st.success: + self.set_exception(st.exception()) # st._exception + return + + if all(st.done for st in self.all_statuses) and all( + st.success for st in self.all_statuses + ): + self.set_finished() + + for st in self.all_statuses: + with st._lock: + st.add_callback(inner) + + def __repr__(self): + return "({self.left!r} & {self.right!r})".format(self=self) + + def __str__(self): + return "{0}(done={1.done}, " "success={1.success})" "".format(self.__class__.__name__, self) + + def __contains__(self, status: StatusBase) -> bool: + for child in [self.left, self.right]: + if child == status: + return True + if isinstance(child, AndStatus): + if status in child: + return True + + return False + + def _run_callbacks(self): + """ + Set the Event and run the callbacks. + """ + if self.timeout is None: + timeout = None + else: + timeout = self.timeout + self.settle_time + if not self._settled_event.wait(timeout): + # We have timed out. It's possible that set_finished() has already + # been called but we got here before the settle_time timer expired. + # And it's possible that in this space be between the above + # statement timing out grabbing the lock just below, + # set_exception(exc) has been called. Both of these possibilties + # are accounted for. + self.log.warning("%r has timed out", self) + with self._externally_initiated_completion_lock: + # Set the exception and mark the Status as done, unless + # set_exception(exc) was called externally before we grabbed + # the lock. + if self._exception is None: + exc = TimeoutError( + f"Status with name {self.name} failed to complete in specified timeout of {self.timeout + self.settle_time}." + ) + self._exception = exc + # Mark this as "settled". + try: + self._settled() + except Exception: + # No alternative but to log this. We can't supersede set_exception, + # and we have to continue and run the callbacks. + self.log.exception("%r encountered error during _settled()", self) + # Now we know whether or not we have succeed or failed, either by + # timeout above or by set_exception(exc), so we can set the Event that + # will mark this Status as done. + with self._lock: + self._event.set() + if self._exception is not None: + try: + self._handle_failure() + except Exception: + self.log.exception("%r encountered an error during _handle_failure()", self) + # The callbacks have access to self, from which they can distinguish + # success or failure. + for cb in self._callbacks: + try: + cb(self) + except Exception: + self.log.exception( + "An error was raised on a background thread while " + "running the callback %r(%r).", + cb, + self, + ) + self._callbacks.clear() diff --git a/debye_bec/devices/pilatus_curtain.py b/debye_bec/devices/pilatus_curtain.py index 94c05b8..4f8ab4e 100644 --- a/debye_bec/devices/pilatus_curtain.py +++ b/debye_bec/devices/pilatus_curtain.py @@ -1,75 +1,102 @@ """ES2 Pilatus Curtain""" -import time +from __future__ import annotations + +import enum +from typing import TYPE_CHECKING from ophyd import Component as Cpt -from ophyd import Device, EpicsSignal, EpicsSignalRO, Kind -from ophyd_devices.utils import bec_utils +from ophyd import EpicsSignal, EpicsSignalRO +from ophyd_devices import CompareStatus, DeviceStatus +from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase + +from debye_bec.devices.pilatus.utils import AndStatusWithList + +if TYPE_CHECKING: + from bec_lib.devicemanager import ScanInfo -class GasMixSetup(Device): +class PilatusCurtainError(Exception): + """PilatusCurtain specific exception""" + + +class COVER(int, enum.Enum): + """Pilatus Curtain States""" + + # TODO What are the proper states here? - Probably enums for the states are better. + OPEN = 0 + CLOSED = 0 + ERROR = 1 + + +class PilatusCurtain(PSIDeviceBase): """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 + def __init__(self, *, name: str, prefix: str = "", scan_info: ScanInfo | None = None, **kwargs): + super().__init__(name=name, prefix=prefix, scan_info=scan_info, **kwargs) 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() + def on_connected(self) -> None: + """ + Called after the device is connected and its signals are connected. + Default values for signals should be set here. + """ + if self.cover_error.get() == COVER.ERROR: + raise PilatusCurtainError("Pilatus Curtain is in an error state!") - self.connector = self.device_manager.connector + def on_stage(self) -> DeviceStatus | None: + """Called while staging the device.""" + return self.open() - def open(self) -> None: + def on_unstage(self) -> DeviceStatus | None: + """Called while unstaging the device.""" + return self.close() + + def on_stop(self) -> DeviceStatus | None: + """Called when the device is stopped.""" + return self.close() + + def open(self) -> DeviceStatus | None: """Open the cover""" + if self.cover_is_closed.get() == COVER.CLOSED: + self.open_cover.put(1) + # TODO timeout ok? + status_open = CompareStatus(self.cover_is_open, COVER.OPEN, timeout=5) + status_error = CompareStatus(self.cover_error, COVER.ERROR, operation="!=") + status = AndStatusWithList(device=self, status_list=[status_open, status_error]) + return status + else: + return None - 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: + def close(self) -> DeviceStatus | 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" - ) + if self.cover_is_open.get() == COVER.OPEN: + self.close_cover.put(1) + # TODO timeout ok? + status_close = CompareStatus(self.cover_is_closed, COVER.CLOSED, timeout=5) + status_error = CompareStatus(self.cover_error, COVER.ERROR, operation="!=") + status = AndStatusWithList(device=self, status_list=[status_close, status_error]) + return status + else: + return None diff --git a/debye_bec/scans/mono_bragg_scans.py b/debye_bec/scans/mono_bragg_scans.py index 1dc131b..03c234b 100644 --- a/debye_bec/scans/mono_bragg_scans.py +++ b/debye_bec/scans/mono_bragg_scans.py @@ -108,8 +108,9 @@ class XASSimpleScanWithXRD(XASSimpleScan): gui_config = { "Movement Parameters": ["start", "stop"], "Scan Parameters": ["scan_time", "scan_duration"], - "Low Energy Range": ["xrd_enable_low", "num_trigger_low", "exp_time_low", "cycle_low"], - "High Energy Range": ["xrd_enable_high", "num_trigger_high", "exp_time_high", "cycle_high"], + "Low Energy Break": ["break_enable_low", "break_time_low", "cycle_low"], + "High Energy Break": ["break_enable_high", "break_time_high", "cycle_high"], + "XRD Triggers": ["exp_time", "n_of_trigger"], } def __init__( @@ -118,14 +119,14 @@ class XASSimpleScanWithXRD(XASSimpleScan): stop: float, scan_time: float, scan_duration: float, - xrd_enable_low: bool, - num_trigger_low: int, - exp_time_low: float, + break_enable_low: bool, + break_time_low: float, cycle_low: int, - xrd_enable_high: bool, - num_trigger_high: int, - exp_time_high: float, + break_enable_high: bool, + break_time_high: float, cycle_high: float, + exp_time: float, + n_of_trigger: int, motor: DeviceBase = "mo1_bragg", **kwargs, ): @@ -138,16 +139,16 @@ class XASSimpleScanWithXRD(XASSimpleScan): stop (float): Stop energy for the scan. scan_time (float): Time for one oscillation . scan_duration (float): Total duration of the scan. - xrd_enable_low (bool): Enable XRD triggering for the low energy range. - num_trigger_low (int): Number of triggers for the low energy range. - exp_time_low (float): Exposure time for the low energy range. + break_enable_low (bool): Enable breaks for the low energy range. + break_time_low (float): Break time for the low energy range. cycle_low (int): Specify how often the triggers should be considered, every nth cycle for low - xrd_enable_high (bool): Enable XRD triggering for the high energy range. - num_trigger_high (int): Number of triggers for the high energy range. - exp_time_high (float): Exposure time for the high energy range. + break_enable_high (bool): Enable breaks for the high energy range. + break_time_high (float): Break time for the high energy range. cycle_high (int): Specify how often the triggers should be considered, every nth cycle for high + exp_time (float): Length of 1 trigger period in seconds + n_of_trigger (int): Amount of triggers to be fired during break motor (DeviceBase, optional): Motor device to be used for the scan. Defaults to "mo1_bragg". @@ -162,14 +163,14 @@ class XASSimpleScanWithXRD(XASSimpleScan): motor=motor, **kwargs, ) - self.xrd_enable_low = xrd_enable_low - self.num_trigger_low = num_trigger_low - self.exp_time_low = exp_time_low + self.break_enable_low = break_enable_low + self.break_time_low = break_time_low self.cycle_low = cycle_low - self.xrd_enable_high = xrd_enable_high - self.num_trigger_high = num_trigger_high - self.exp_time_high = exp_time_high + self.break_enable_high = break_enable_high + self.break_time_high = break_time_high self.cycle_high = cycle_high + self.exp_time = exp_time + self.n_of_trigger = n_of_trigger class XASAdvancedScan(XASSimpleScan): @@ -233,8 +234,9 @@ class XASAdvancedScanWithXRD(XASAdvancedScan): "Movement Parameters": ["start", "stop"], "Scan Parameters": ["scan_time", "scan_duration"], "Spline Parameters": ["p_kink", "e_kink"], - "Low Energy Range": ["xrd_enable_low", "num_trigger_low", "exp_time_low", "cycle_low"], - "High Energy Range": ["xrd_enable_high", "num_trigger_high", "exp_time_high", "cycle_high"], + "Low Energy Break": ["break_enable_low", "break_time_low", "cycle_low"], + "High Energy Break": ["break_enable_high", "break_time_high", "cycle_high"], + "XRD Triggers": ["exp_time", "n_of_trigger"], } def __init__( @@ -245,14 +247,14 @@ class XASAdvancedScanWithXRD(XASAdvancedScan): scan_duration: float, p_kink: float, e_kink: float, - xrd_enable_low: bool, - num_trigger_low: int, - exp_time_low: float, + break_enable_low: bool, + break_time_low: float, cycle_low: int, - xrd_enable_high: bool, - num_trigger_high: int, - exp_time_high: float, + break_enable_high: bool, + break_time_high: float, cycle_high: float, + exp_time: float, + n_of_trigger: int, motor: DeviceBase = "mo1_bragg", **kwargs, ): @@ -270,16 +272,16 @@ class XASAdvancedScanWithXRD(XASAdvancedScan): scan_duration (float): Total duration of the scan. p_kink (float): Position of kink. e_kink (float): Energy of the kink. - xrd_enable_low (bool): Enable XRD triggering for the low energy range. - num_trigger_low (int): Number of triggers for the low energy range. - exp_time_low (float): Exposure time for the low energy range. + break_enable_low (bool): Enable breaks for the low energy range. + break_time_low (float): Break time for the low energy range. cycle_low (int): Specify how often the triggers should be considered, every nth cycle for low - xrd_enable_high (bool): Enable XRD triggering for the high energy range. - num_trigger_high (int): Number of triggers for the high energy range. - exp_time_high (float): Exposure time for the high energy range. + break_enable_high (bool): Enable breaks for the high energy range. + break_time_high (float): Break time for the high energy range. cycle_high (int): Specify how often the triggers should be considered, every nth cycle for high + exp_time (float): Length of 1 trigger period in seconds + n_of_trigger (int): Amount of triggers to be fired during break motor (DeviceBase, optional): Motor device to be used for the scan. Defaults to "mo1_bragg". @@ -298,11 +300,11 @@ class XASAdvancedScanWithXRD(XASAdvancedScan): ) self.p_kink = p_kink self.e_kink = e_kink - self.xrd_enable_low = xrd_enable_low - self.num_trigger_low = num_trigger_low - self.exp_time_low = exp_time_low + self.break_enable_low = break_enable_low + self.break_time_low = break_time_low self.cycle_low = cycle_low - self.xrd_enable_high = xrd_enable_high - self.num_trigger_high = num_trigger_high - self.exp_time_high = exp_time_high + self.break_enable_high = break_enable_high + self.break_time_high = break_time_high self.cycle_high = cycle_high + self.exp_time = exp_time + self.n_of_trigger = n_of_trigger diff --git a/test_commit.yml b/test_commit.yml new file mode 100644 index 0000000..e69de29 diff --git a/test_commit_2.yml b/test_commit_2.yml new file mode 100644 index 0000000..e69de29 diff --git a/test_commit_3.yml b/test_commit_3.yml new file mode 100644 index 0000000..e69de29 diff --git a/test_commit_4 b/test_commit_4 new file mode 100644 index 0000000..e69de29 diff --git a/tests/tests_devices/test_mo1_bragg.py b/tests/tests_devices/test_mo1_bragg.py index ac7066b..9dc2e31 100644 --- a/tests/tests_devices/test_mo1_bragg.py +++ b/tests/tests_devices/test_mo1_bragg.py @@ -132,10 +132,12 @@ def test_set_trig_settings(mock_bragg): dev.set_trig_settings( enable_low=True, enable_high=False, - exp_time_high=0.1, - exp_time_low=0.01, + break_time_high=0.1, + break_time_low=0.01, cycle_low=1, cycle_high=3, + exp_time=0.5, + n_of_trigger=7, ) assert dev.scan_settings.trig_ena_lo_enum.get() == True assert dev.scan_settings.trig_ena_hi_enum.get() == False @@ -143,6 +145,8 @@ def test_set_trig_settings(mock_bragg): assert dev.scan_settings.trig_every_n_hi.get() == 3 assert dev.scan_settings.trig_time_lo.get() == 0.01 assert dev.scan_settings.trig_time_hi.get() == 0.1 + assert dev.trigger_settings.xrd_trig_period.get() == 0.5 + assert dev.trigger_settings.xrd_n_of_trig.get() == 7 def test_set_control_settings(mock_bragg): diff --git a/tests/tests_devices/test_pilatus.py b/tests/tests_devices/test_pilatus.py new file mode 100644 index 0000000..1ad3347 --- /dev/null +++ b/tests/tests_devices/test_pilatus.py @@ -0,0 +1,285 @@ +# pylint: skip-file +import os +import threading +from typing import TYPE_CHECKING, Generator +from unittest import mock + +import numpy as np +import ophyd +import pytest +from bec_lib.messages import ScanStatusMessage +from bec_server.scan_server.scan_worker import ScanWorker +from ophyd_devices import CompareStatus, DeviceStatus +from ophyd_devices.interfaces.base_classes.psi_device_base import DeviceStoppedError +from ophyd_devices.tests.utils import MockPV, patch_dual_pvs +from ophyd_devices.utils.psi_device_base_utils import TaskStatus + +from debye_bec.devices.pilatus.pilatus import ( + ACQUIREMODE, + COMPRESSIONALGORITHM, + DETECTORSTATE, + FILEWRITEMODE, + TRIGGERMODE, + Pilatus, +) + +if TYPE_CHECKING: # pragma no cover + from bec_lib.messages import FileMessage + +# @pytest.fixture(scope="function") +# def scan_worker_mock(scan_server_mock): +# scan_server_mock.device_manager.connector = mock.MagicMock() +# scan_worker = ScanWorker(parent=scan_server_mock) +# yield scan_worker + + +@pytest.fixture( + scope="function", + params=[ + (0.1, 1, 1, "line_scan", "step"), + (0.2, 2, 2, "time_scan", "step"), + (0.5, 5, 5, "xas_advanced_scan", "fly"), + ], +) +def mock_scan_info(request, tmpdir): + exp_time, frames_per_trigger, num_points, scan_name, scan_type = request.param + scan_info = ScanStatusMessage( + scan_id="test_id", + status="open", + scan_type=scan_type, + scan_number=1, + scan_parameters={ + "exp_time": exp_time, + "frames_per_trigger": frames_per_trigger, + "system_config": {}, + }, + info={"file_components": (f"{tmpdir}/data/S00000/S000001", "h5")}, + num_points=num_points, + scan_name=scan_name, + ) + yield scan_info + + +@pytest.fixture(scope="function") +def pilatus(mock_scan_info) -> Generator[Pilatus, None, None]: + name = "pilatus" + prefix = "X01DA-OP-MO1:PILATUS:" + with mock.patch.object(ophyd, "cl") as mock_cl: + mock_cl.get_pv = MockPV + mock_cl.thread_class = threading.Thread + dev = Pilatus(name=name, prefix=prefix) + patch_dual_pvs(dev) + # dev.image1 = mock.MagicMock() + # with mock.patch.object(dev, "image1"): + with mock.patch.object(dev, "task_handler"): + dev.scan_info.msg = mock_scan_info + try: + yield dev + finally: + try: + dev.on_destroy() + except ophyd.utils.DestroyedError: + pass + + +# TODO figure out how to test as set calls on the PV below seem to break it.. +# def test_pilatus_on_connected(pilatus): +# """Test the on_connected logic of the Pilatus detector.""" +# pilatus.cam.acquire._read_pv.mock_data = ACQUIREMODE.DONE.value +# pilatus.hdf.capture._read_pv.mock_data = ACQUIREMODE.DONE.value +# pilatus.on_connected() +# assert pilatus.cam.trigger_mode.get() == TRIGGERMODE.MULT_TRIGGER +# assert pilatus.hdf.file_write_mode.get() == FILEWRITEMODE.STREAM +# assert pilatus.hdf.file_template.get() == "%s%s" +# assert pilatus.hdf.auto_save.get() == 1 +# assert pilatus.hdf.lazy_open.get() == 1 +# assert pilatus.hdf.compression.get() == COMPRESSIONALGORITHM.NONE + + +def test_pilatus_on_stop(pilatus): + """Test the on_stop logic of the Pilatus detector.""" + pilatus.cam.acquire._read_pv.mock_data = ACQUIREMODE.ACQUIRING.value + pilatus.hdf.capture._read_pv.mock_data = ACQUIREMODE.ACQUIRING.value + pilatus.on_stop() + assert pilatus.cam.acquire.get() == ACQUIREMODE.DONE + assert pilatus.hdf.capture.get() == ACQUIREMODE.DONE + + +def test_pilatus_on_destroy(pilatus): + """Test the on_destroy logic of the Pilatus detector.""" + with mock.patch.object(pilatus, "on_stop") as mock_on_stop: + pilatus.destroy() + assert mock_on_stop.call_count == 1 + assert pilatus._poll_thread_kill_event.is_set() + + +def test_pilatus_on_failure_callback(pilatus): + """Test the on_failure_callback logic of the Pilatus detector.""" + + with mock.patch.object(pilatus, "on_stop") as mock_on_stop: + status = DeviceStatus(pilatus) + status.set_finished() # Does not trigger 'stop' + assert mock_on_stop.call_count == 0 + status = DeviceStatus(pilatus) + status.set_exception(RuntimeError("Test error")) # triggers 'stop' + assert mock_on_stop.call_count == 1 + + +def test_pilatus_on_pre_scan(pilatus): + """Test the on_pre_scan logic of the Pilatus detector.""" + scan_msg = pilatus.scan_info.msg + if scan_msg.scan_type != "step" and scan_msg.scan_name not in pilatus.xas_xrd_scan_names: + assert pilatus.on_pre_scan() is None + return + pilatus.cam.acquire._read_pv.mock_data = ACQUIREMODE.DONE.value + pilatus.hdf.capture._read_pv.mock_data = ACQUIREMODE.DONE.value + pilatus.cam.armed._read_pv.mock_data = DETECTORSTATE.UNARMED.value + status = pilatus.on_pre_scan() + assert status.done is False + pilatus.cam.armed.put(DETECTORSTATE.ARMED.value) + status.wait(timeout=5) + assert status.done is True + assert status.success is True + + +def test_pilatus_on_trigger(pilatus): + """test on trigger logic of the Pilatus detector.""" + scan_msg = pilatus.scan_info.msg + if scan_msg.scan_type != "step" and scan_msg.scan_name not in pilatus.xas_xrd_scan_names: + status = pilatus.trigger() + assert status.done is True + assert status.success is True + return None + pilatus.hdf.num_captured._read_pv.mock_data = 0 + pilatus.trigger_shot.put(0) + status = pilatus.trigger() + assert status.done is False + assert pilatus.trigger_shot.get() == 1 + pilatus.hdf.num_captured._read_pv.mock_data = 1 + status.wait(timeout=5) + assert status.done is True + assert status.success is True + + +def test_pilatus_on_trigger_cancel_on_stop(pilatus): + """Test that the status of the trigger is cancelled if stop is called""" + if pilatus.scan_info.msg.scan_name.startswith("xas"): + status = pilatus.trigger() + assert status.done is True + assert status.success is True + return + pilatus.hdf.num_captured._read_pv.mock_data = 0 + pilatus.trigger_shot.put(0) + status = pilatus.trigger() + assert status.done is False + with pytest.raises(DeviceStoppedError): + pilatus.stop() + status.wait(timeout=5) + + +def test_pilatus_on_complete(pilatus): + """Test the on_complete logic of the Pilatus detector.""" + + if pilatus.scan_info.msg.scan_name.startswith("xas"): + # TODO add test cases for xas scans + # status = pilatus.complete() + # assert status.done is True + # assert status.success is True + return + # Check in addition that the file event is set properly, once with if it works, and once if not (i.e. when cancelled) + for success in [True, False]: + if success is True: + pilatus.file_event.put(file_path="", done=False, successful=False) + pilatus._full_path = "file-path-for-success" + else: + pilatus.file_event.put(file_path="", done=False, successful=True) + pilatus._full_path = "file-path-for-failure" + # Set values for relevant PVs + pilatus.cam.acquire._read_pv.mock_data = ACQUIREMODE.ACQUIRING.value + pilatus.hdf.capture._read_pv.mock_data = ACQUIREMODE.ACQUIRING.value + pilatus.cam.armed._read_pv.mock_data = DETECTORSTATE.ARMED.value + num_images = pilatus.scan_info.msg.num_points * pilatus.scan_info.msg.scan_parameters.get( + "frames_per_trigger", 1 + ) + pilatus.hdf.num_captured._read_pv.mock_data = num_images - 1 + # Call on complete + pilatus.n_images = num_images + status = pilatus.complete() + # Should not be finished + assert status.done is False + pilatus.cam.acquire.put(ACQUIREMODE.DONE.value) + pilatus.hdf.capture.put(ACQUIREMODE.DONE.value) + pilatus.cam.armed.put(DETECTORSTATE.UNARMED.value) + assert status.done is False + if success is True: + pilatus.hdf.num_captured._read_pv.mock_data = num_images + # Now it should resolve + status.wait(timeout=5) + assert status.done is True + assert status.success is True + file_msg: FileMessage = pilatus.file_event.get() + assert file_msg.file_path == "file-path-for-success" + assert file_msg.done is True + assert file_msg.successful is True + else: + with pytest.raises(DeviceStoppedError): + pilatus.stop() + status.wait(timeout=5) + assert status.done is True + assert status.success is False + file_msg: FileMessage = pilatus.file_event.get() + assert file_msg.file_path == "file-path-for-failure" + assert file_msg.done is True + assert file_msg.successful is False + + +# TODO, figure out how to properly test this.. +# def test_pilatus_on_stage(pilatus): +# """Test the on_stage logic of the Pilatus detector.""" +# # Make sure that no additional logic from stage signals of underlying components is triggered +# pilatus.stage_sigs = {} +# pilatus.cam.stage_sigs = {} +# pilatus.hdf.stage_sigs = {} +# if pilatus.scan_info.msg.scan_name.startswith("xas"): +# pilatus.on_stage() +# return +# exp_time = pilatus.scan_info.msg.scan_parameters.get("exp_time", 0.1) +# n_images = pilatus.scan_info.msg.num_points * pilatus.scan_info.msg.scan_parameters.get( +# "frames_per_trigger", 1 +# ) +# if exp_time <= 0.1: +# with pytest.raises(ValueError): +# pilatus.on_stage() +# return +# pilatus.filter_number.put(10) +# pilatus.cam.array_counter.put(1) +# file_components = pilatus.scan_info.msg.info.get("file_components", ("", ""))[0] +# base_path = file_components[0].rsplit("/", 1)[0] +# file_name = file_components[0].rsplit("/", 1)[1] + "_pilatus.h5" +# file_path = os.path.join(base_path, file_name) +# pilatus.on_stage() +# assert pilatus.cam.array_callbacks.get() == 0 +# assert pilatus.hdf.enable.get() == 1 +# assert pilatus.cam.num_exposures.get() == 1 +# assert pilatus.cam.num_images.get() == n_images +# assert pilatus.cam.acquire_time.get() == exp_time - pilatus._readout_time +# assert pilatus.cam.acquire_period.get() == exp_time +# assert pilatus.filter_number.get() == 0 +# assert pilatus.hdf.file_path.get() == base_path +# assert pilatus.hdf.file_name.get() == file_name +# assert pilatus.hdf.num_capture.get() == n_images +# assert pilatus.cam.array_counter.get() == 0 +# file_msg: FileMessage = pilatus.file_event.get() +# assert file_msg.file_path == file_path +# assert file_msg.done is False +# assert file_msg.successful is False + + +def test_pilatus_on_stage_raises_low_exp_time(pilatus): + """Test that on_stage raises a ValueError if the exposure time is too low.""" + pilatus.scan_info.msg.scan_parameters["exp_time"] = 0.09 + scan_msg = pilatus.scan_info.msg + if scan_msg.scan_type != "step" and scan_msg.scan_name not in pilatus.xas_xrd_scan_names: + return + with pytest.raises(ValueError): + pilatus.on_stage() diff --git a/tests/tests_scans/test_mono_bragg_scans.py b/tests/tests_scans/test_mono_bragg_scans.py index 52dec76..e0bafd6 100644 --- a/tests/tests_scans/test_mono_bragg_scans.py +++ b/tests/tests_scans/test_mono_bragg_scans.py @@ -141,17 +141,18 @@ def test_xas_simple_scan_with_xrd(scan_assembler, ScanStubStatusMock): stop=5, scan_time=1, scan_duration=10, - xrd_enable_low=True, - num_trigger_low=1, - exp_time_low=1, + break_enable_low=True, + break_time_low=1, cycle_low=1, - xrd_enable_high=True, - num_trigger_high=2, - exp_time_high=3, + break_enable_high=True, + break_time_high=2, + exp_time=1, + n_of_trigger=1, cycle_high=4, ) request.device_manager.add_device("nidaq") reference_commands = get_instructions(request, ScanStubStatusMock) + # TODO #64 based on creating this ScanStatusMessage, we should test the logic of stage/kickoff/complete/unstage in Pilatus and mo1Bragg assert reference_commands == [ None, @@ -339,13 +340,13 @@ def test_xas_advanced_scan_with_xrd(scan_assembler, ScanStubStatusMock): scan_duration=10, p_kink=50, e_kink=8500, - xrd_enable_low=True, - num_trigger_low=1, - exp_time_low=1, + break_enable_low=True, + break_time_low=1, cycle_low=1, - xrd_enable_high=True, - num_trigger_high=2, - exp_time_high=3, + break_enable_high=True, + break_time_high=2, + exp_time=1, + n_of_trigger=1, cycle_high=4, ) request.device_manager.add_device("nidaq")