From 3df08bf6dc00c80b7e621d6de89e0a01f49cb759 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Mon, 16 Jun 2025 16:28:43 +0200 Subject: [PATCH 01/42] fix(config): add devices to superxas config --- .../device_configs/x10da_config_250616.yaml | 756 ++++++++++++++++++ 1 file changed, 756 insertions(+) create mode 100644 superxas_bec/device_configs/x10da_config_250616.yaml diff --git a/superxas_bec/device_configs/x10da_config_250616.yaml b/superxas_bec/device_configs/x10da_config_250616.yaml new file mode 100644 index 0000000..2e174b7 --- /dev/null +++ b/superxas_bec/device_configs/x10da_config_250616.yaml @@ -0,0 +1,756 @@ +absorption_transmission: + deviceClass: ophyd_devices.ComputedSignal + deviceConfig: + compute_method: "def compute_signals(signal1, signal2):\n import math\n return\ + \ math.log(signal1.get()/signal2.get())\n" + input_signals: + - ic1 + - ic2 + enabled: true + readOnly: false + readoutPriority: monitored +bm2_tr1: + description: Beam Monitor 2 Translation 1 + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-BM2:TR1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +bm2_tr2: + description: Beam Monitor 2 Translation 2 + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-BM2:TR2 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +bm3_tr1: + description: Beam Monitor 3 Translation 1 + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-BM3:TR1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +bm3_tr2: + description: Beam Monitor 3 Translation 2 + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-BM3:TR2 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_bnd: + description: Collimating Mirror bender + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:BND + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_pitch: + description: Collimating Mirror Pitch + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:ROTX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_roll: + description: Collimating Mirror Roll + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:ROTZ + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_trx: + description: Collimating Mirror X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:XTCP + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_trxd: + description: Collimating Mirror X-translation downstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:TRXD + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_trxu: + description: Collimating Mirror X-translation upstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:TRXU + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_try: + description: Collimating Mirror Point Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:YTCP + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_trydr: + description: Collimating Mirror Y-translation downstream ring + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:TRYDR + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_trydw: + description: Collimating Mirror Y-translation downstream wall + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:TRYDW + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_tryu: + description: Collimating Mirror Y-translation upstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:TRYU + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_yaw: + description: Collimating Mirror Yaw + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:ROTY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +falcon: + description: Falcon Sitoro detector + deviceClass: superxas_bec.devices.falcon.FalconSuperXAS + deviceConfig: + prefix: 'X10DA-SITORO:' + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +filter_fe: + description: Front End Filter + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-FI:TRY1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_bnd: + description: Focusing Mirror bender + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRB + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_pitch: + description: Focusing Mirror Pitch + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:pitch + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_roll: + description: Focusing Mirror Roll + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:roll + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_trx: + description: Focusing Mirror X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:trans + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_trxd: + description: Focusing Mirror X-translation downstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRXD + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_trxu: + description: Focusing Mirror X-translation upstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRXU + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_try: + description: Focusing Mirror Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:y + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_trydr: + description: Focusing Mirror Y-translation downstream ring + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRYDR + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_trydw: + description: Focusing Mirror Y-translation downstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRYDW + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_tryu: + description: Focusing Mirror Y-translation upstream wall + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRYU + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_yaw: + description: Focusing Mirror Yaw + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:yaw + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +hrm_rotx: + description: Harmonic Rejection Mirror X-Rotation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-HRM:ROX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +hrm_try: + description: Harmonic Rejection Mirror Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-HRM:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +ic1: + description: Ionization Chamber 1 + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-ES1-SAI_01:MEAN + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +ic1_try: + description: Ionization Chamber 1 Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-IC1:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +ic2: + description: Ionization Chamber 2 + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-ES1-SAI_02:MEAN + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +ic2_try: + description: Ionization Chamber 2 Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-IC2:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +ic3: + description: Ionization Chamber 3 + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-ES1-SAI_03:MEAN + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +ic3_try: + description: Ionization Chamber 3 Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-IC3:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +ic4: + description: Ionization Chamber 4 + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-ES1-SAI_04:MEAN + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +manip_heavy_trx: + description: Heavy Sample Manipulator Motor X + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-PP1:MOT1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_heavy_try: + description: Heavy Sample Manipulator Motor Y + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-PP1:MOT2 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_heavy_trz: + description: Heavy Sample Manipulator Motor Z + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-PP1:MOT3 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_new_rot: + description: Old Sample Manipulator Base rotation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MAN:ROTY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_new_trx: + description: Old Sample Manipulator X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MAN:TRX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_new_try: + description: Old Sample Manipulator Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MAN:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_new_trz: + description: Old Sample Manipulator Z - Along beam + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MAN:TRZ + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_old_rot: + description: Old Sample Manipulator Base rotation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MA1:TRX2 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_old_trx: + description: Old Sample Manipulator X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MA1:TRX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_old_try: + description: Old Sample Manipulator Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MA1:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_old_trz: + description: Old Sample Manipulator Z - Along beam + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MA1:TRX1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +mono_encoder_deg: + description: ROTX encoder of Mono converted to degrees + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-OP1-MO1:ENC-ROTXCalc.VAL + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +mono_encoder_raw: + description: ROTX encoder of Mono raw value + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-OP1-MO1:ENC-ROTX.RVAL + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +mono_energy: + description: Mono Energy + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-MO12-QEXAFS:E_TEST + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +mono_rotx: + description: Monochromator 1 X-Rotation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP1-MO1:ROTX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +mono_trx: + description: Monochromator 1 X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP1-MO1:TRX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +mono_try: + description: Monochromator 1 Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP1-MO1:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_centerx: + description: Front-end slit diaphragm X-center + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:CENTERX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_centery: + description: Front-end slit diaphragm Y-center + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:CENTERY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_gapx: + description: Front-end slit diaphragm X-gap + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:GAPX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_gapy: + description: Front-end slit diaphragm Y-gap + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:GAPY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_trxr: + description: Front-end slit diaphragm 1 X-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:TRXR + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_trxw: + description: Front-end slit diaphragm 2 X-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:TRXW + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_tryb: + description: Front-end slit diaphragm 1 Y-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:TRYB + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_tryt: + description: Front-end slit diaphragm 2 Y-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:TRYT + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_kb_centerx: + description: KB slit axis X + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-SH1:POSX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_kb_centery: + description: KB slit axis Y + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-SV1:POSY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_kb_gapx: + description: KB slit axis X + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-SH1:OPENX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_kb_gapy: + description: KB slit axis Y + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-SV1:OPENY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_centerx: + description: Optics slit X-center + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SHtestCENTER + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_centery: + description: Optics slit Y-center + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SVcenter + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_gapx: + description: Optics slit X-gap + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SHsize + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_gapy: + description: Optics slit Y-gap + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SVsize + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_htr1: + description: Optics slit X-translation Ring + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SH1:TR1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_htr2: + description: Optics slit X-translation Wall + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SH1:TR2 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_vtr1: + description: Optics slit Y-translation Up + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SV1:TR1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_vtr2: + description: Optics slit Y-translation Down + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SV1:TR2 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +t_absorber1: + description: AbsorberTemperature 1 + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-FE-ABS1-ETTC-0010:TEMP + enabled: false + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +table1_trx: + description: Experimental Table 1 X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-ET1:TRX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +table1_try: + description: Experimental Table 1 Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-ET1:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +table2_trx: + description: Experimental Table 2 X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES2-ET2:TRX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +table2_try: + description: Experimental Table 2 Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES2-ET2:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +trigger: + description: Trigger Card + deviceClass: superxas_bec.devices.trigger.Trigger + deviceConfig: + prefix: 'X10DA-ES1:' + enabled: true + onFailure: raise + readoutPriority: baseline + softwareTrigger: true +xrayeye_foc: + description: X-Ray Eye Fine Focus + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-XE1:FINFOC + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +xrayeye_zoom: + description: X-Ray Eye Zoom + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-XE1:ZOOM + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false -- 2.49.1 From 6946e34aca204db4cf0c4555d4a211e6e557c3d7 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Mon, 16 Jun 2025 16:29:59 +0200 Subject: [PATCH 02/42] fix(exafs-scan): change default motor --- superxas_bec/scans/exafs_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superxas_bec/scans/exafs_scan.py b/superxas_bec/scans/exafs_scan.py index a73e0f2..6f463c1 100644 --- a/superxas_bec/scans/exafs_scan.py +++ b/superxas_bec/scans/exafs_scan.py @@ -39,7 +39,7 @@ class EXAFSScan(ScanBase): self.k_step_conversion = 3.81 self._check_and_upated_input_arguments() if motor is None: - default_motor = "kb_slit_y" + default_motor = "mono_energy" motor = default_motor # TODO Remove that motor, put energy of mono self.motor = motor super().__init__(exp_time=0, relative=False, settling_time=settling_time, **kwargs) -- 2.49.1 From f1fc436dda35a2b9ec9b2a028a77fd6ea08831a9 Mon Sep 17 00:00:00 2001 From: appel_c Date: Tue, 17 Jun 2025 10:19:25 +0200 Subject: [PATCH 03/42] refactor(trigger): Review trigger class integration --- superxas_bec/devices/trigger.py | 59 +++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/superxas_bec/devices/trigger.py b/superxas_bec/devices/trigger.py index 21e7c06..77af731 100644 --- a/superxas_bec/devices/trigger.py +++ b/superxas_bec/devices/trigger.py @@ -9,8 +9,11 @@ logger = bec_logger.logger import enum from bec_lib.devicemanager import ScanInfo +from ophyd_devices import CompareStatus, TransitionStatus from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase +from superxas_bec.devices.falcon import Falcon, FalconAcquiringStatus + class ContinuousSamplingMode(int, enum.Enum): """Options for start_csmpl signal""" @@ -36,14 +39,22 @@ class TriggerControl(Device): doc="Number of cycles (multiplies by 0.2s)", ) start_csmpl = Cpt( - EpicsSignal, suffix="START-CSMPL", kind=Kind.config, doc="Continous sampling mode on/off" + EpicsSignal, + suffix="START-CSMPL", + kind=Kind.config, + doc="Continous sampling mode on/off", + put_complete=True, ) smpl = Cpt( EpicsSignal, suffix="SMPL", kind=Kind.config, doc="Sampling Trigger if cont mode is off" ) smpl_done = Cpt( - EpicsSignalRO, suffix="SMPL-DONE", kind=Kind.config, doc="Done status of trigger" - ) + EpicsSignalRO, + suffix="SMPL-DONE", + kind=Kind.config, + auto_monitor=True, + doc="Done status of trigger", + ) # Status PV updates need to be monitored as we wait and react for changes class Trigger(PSIDeviceBase, TriggerControl): @@ -60,6 +71,7 @@ class Trigger(PSIDeviceBase, TriggerControl): super().__init__(name=name, prefix=prefix, scan_info=scan_info, **kwargs) self.device_manager = device_manager self._pv_timeout = 1 + self._falcon_ready_timeout = 5 # seconds ######################################## # Beamline Specific Implementations # @@ -87,41 +99,40 @@ class Trigger(PSIDeviceBase, TriggerControl): """ self.start_csmpl.set(ContinuousSamplingMode.OFF).wait(timeout=self._pv_timeout) exp_time = self.scan_info.msg.scan_parameters["exp_time"] - if self.scan_info.msg.scan_name != "exafs_scan": - self.set_exposure_time(exp_time).wait() + if self.scan_info.msg.scan_name != "exafs_scan": # TODO + self.set_exposure_time(exp_time).wait(self._pv_timeout) def on_unstage(self) -> DeviceStatus | StatusBase | None: """Called while unstaging the device.""" - status = self.start_csmpl.set(ContinuousSamplingMode.ON) + status = self.start_csmpl.set(ContinuousSamplingMode.ON).wait(timeout=self._pv_timeout) return status def on_pre_scan(self) -> DeviceStatus | StatusBase | None: """Called right before the scan starts on all devices automatically.""" def on_trigger(self) -> DeviceStatus | StatusBase | None: - """Called when the device is triggered.""" + """ + Called when the device is triggered. + If Falcon is present in the device manager, it will also wait for the Falcon to be ready for acquiring, + and then wait for the Falcon to be done with acquiring. This is to ensure that data of PVs is updated + and the sampling is done before data is being read from the device. + """ + falcon = self.device_manager.devices.get("falcon", None) - if falcon is not None: + falcon: Falcon # pylint: disable=protected-access - status = falcon._stop_erase_and_wait_for_acquiring() - status.wait() - - started = False - - def _sampling_done(): - nonlocal started - if not started and self.smpl_done.get() == SamplingDone.RUNNING: - started = True - return False - if started and self.smpl_done.get() == SamplingDone.DONE: - return True - - return self.smpl_done.get() == SamplingDone.DONE + CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING).wait( + timeout=self._falcon_ready_timeout + ) + status_smpl = TransitionStatus(self.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE]) self.smpl.put(1) - status = self.task_handler.submit_task(_sampling_done, run=True) - return status + # We also need to wait for sampling to be done for the falcon if it's present. + # if falcon is not None: + # status_done = falcon.done_with_acquiring() + # status_done.wait(timeout=self._falcon_ready_timeout) + return status_smpl def on_complete(self) -> DeviceStatus | StatusBase | None: """Called to inquire if a device has completed a scans.""" -- 2.49.1 From 45ad51829007f05d1d1b108228a9cff9c72ffa71 Mon Sep 17 00:00:00 2001 From: appel_c Date: Wed, 18 Jun 2025 08:45:13 +0200 Subject: [PATCH 04/42] refactor: direct implementation of falcon without area_detector --- superxas_bec/devices/falcon_direct.py | 264 ++++++++++++++++++++++++++ superxas_bec/devices/trigger.py | 40 ++-- 2 files changed, 292 insertions(+), 12 deletions(-) create mode 100644 superxas_bec/devices/falcon_direct.py diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py new file mode 100644 index 0000000..e7522ce --- /dev/null +++ b/superxas_bec/devices/falcon_direct.py @@ -0,0 +1,264 @@ +import enum +from collections import OrderedDict +from time import time +from typing import TYPE_CHECKING + +import numpy as np +from bec_lib.devicemanager import ScanInfo +from bec_lib.logger import bec_logger +from ophyd import Component as Cpt +from ophyd import Device +from ophyd import DynamicDeviceComponent as DDC +from ophyd import EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV, Kind, Signal +from ophyd_devices import ( + AsyncSignal, + CompareStatus, + DeviceStatus, + PreviewSignal, + StatusBase, + TransitionStatus, +) +from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase + +logger = bec_logger.logger + + +class FalconAcquiringStatus(int, enum.Enum): + """Status of Falcon""" + + DONE = 0 + ACQUIRING = 1 + + +class DXPControl(Device): + """DXP Control Device for Falcon detector""" + + input_count_rate = Cpt(EpicsSignalRO, "InputCountRate", kind=Kind.omitted, auto_monitor=True) + output_count_rate = Cpt(EpicsSignalRO, "OutputCountRate", kind=Kind.omitted, auto_monitor=True) + elapsed_real_time = Cpt(EpicsSignalRO, "ElapsedRealTime", kind=Kind.omitted, auto_monitor=True) + + +class ROI(Device): + + # 'name' is not an allowed attribute + label = Cpt(EpicsSignal, "NM", lazy=True) + count = Cpt(EpicsSignalRO, "", lazy=True) + net_count = Cpt(EpicsSignalRO, "N", lazy=True) + + +def add_rois(range_, **kwargs): + defn = OrderedDict() + + for roi in range_: + if not (0 <= roi < 32): + raise ValueError("roi must be in the set [0,31]") + + attr = "roi{}".format(roi) + defn[attr] = (ROI, ".R{}".format(roi), kwargs) + + return defn + + +class MCAControl(Device): + """MCA Control Device for Falcon detector""" + + spectrum = Cpt(EpicsSignalRO, ".VAL", kind=Kind.omitted, auto_monitor=True) + rois = DDC(add_rois(range(0, 1), kind="omitted"), kind="omitted") + elapsed_real_time = Cpt(EpicsSignalRO, ".ERTM", kind=Kind.omitted, auto_monitor=True) + + +class DeadTimeCorrectedCounts(Signal): + """Signal to calculate dead time corrected counts""" + + def __init__(self, name: str, channel: int, **kwargs): + """ + Initialize DeadTimeCorrectedCounts signal. + + Args: + name (str): Name of the signal + channel (int): Channel number + """ + super().__init__(name=name, **kwargs) + self._channel = channel + self._dead_time = 1.182e-7 + + # pylint: disable=arguments-differ + def get(self) -> float: + """Get dead time corrected counts base on signals from dxp and mca of Falcon""" + dxp: DXPControl = getattr(self.parent, f"dxp{self._channel}") + mca: MCAControl = getattr(self.parent, f"mca{self._channel}") + + icr = dxp.input_count_rate.get() + ocr = dxp.output_count_rate.get() + roi = mca.rois.roi0.count.get() + ert = mca.elapsed_real_time.get() + print(icr, ocr, roi, ert) + + if icr == 0 or ocr == 0: + return 0 + + # Check that relative change is large enough + test = 1e9 + test_icr = icr + n = 0 + while test > self._dead_time and n < 30: + try: + true_icr = icr * np.exp(test_icr * self._dead_time) + test = (true_icr - test_icr) / test_icr + test_icr = true_icr + n += 1 + except Exception as e: # pylint: disable=broad-except + logger.info(f"Error in computation of signal {self.name}, error: {e}") + return 0 + + # Return corrected roi counts + cor_roi_cnts = 0 + if ocr * ert != 0: + cor_roi_cnts = roi * true_icr / (ocr * ert) + return cor_roi_cnts + + +class FalconControlDirect(Device): + """Stripped implementation of Falcon detector for SuperXAS; prefix: 'X10DA-SITORO:'""" + + # Acquisition control + erase_all = Cpt(EpicsSignal, "EraseAll", kind=Kind.omitted) + erase_start = Cpt(EpicsSignal, "EraseStart", put_complete=True, kind=Kind.omitted) + start_all = Cpt(EpicsSignal, "StartAll", put_complete=True, kind=Kind.omitted) + stop_all = Cpt(EpicsSignal, "StopAll", kind=Kind.omitted) + + collect_mode = Cpt(EpicsSignalWithRBV, "CollectMode", kind=Kind.config) + preset_real_time = Cpt(EpicsSignal, "PresetReal", kind=Kind.config) + acquiring = Cpt(EpicsSignal, "Acquiring", kind=Kind.omitted, auto_monitor=True) + + # DXP parameters + dxp1 = Cpt(DXPControl, "dxp1:") + # dxp2 = Cpt(EpicsDXPFalcon, "dxp2:") + + # MCA record with spectrum data + mca1 = Cpt(MCAControl, "mca1") + # mca2 = Cpt(EpicsMCARecord, "mca2") + + # Norm Signal + dead_time_cor_cnts1 = Cpt( + DeadTimeCorrectedCounts, name="dead_time_cor_cnts", channel=1, kind=Kind.hinted + ) + # dead_time_cor_cnts2 = Cpt(DeadTimeCorrectedCounts, name='dead_time_cor_cnts', channel=2, kind=Kind.normal) + + +class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): + """Falcon implementierung at SuperXAS. prefix: 'X10DA-SITORO:'""" + + preview = Cpt(PreviewSignal, name="preview", ndim=1, doc="Preview signal for Falcon detector") + async_data = Cpt( + AsyncSignal, ndim=0, max_size=int(1e6), doc="Asynchronous data signal for Falcon detector" + ) + + ######################################## + # Beamline Specific Implementations # + ######################################## + + def __init__(self, name: str, prefix: str = "", scan_info: ScanInfo | None = None, **kwargs): + """ + Initialize Falcon device. + + Args: + name (str): Name of the device + prefix (str): Prefix of the device + scan_info (ScanInfo): Information about the scan + **kwargs: Additional keyword arguments + """ + super().__init__(name=name, prefix=prefix, scan_info=scan_info, **kwargs) + self._pv_timeout = 1 + self._async_read_data = [ + "dxp_input_count_rate", + "dxp_output_count_rate", + "mca_elapsed_real_time", + "mca_rois_roi0_count", + ] + + 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. + """ + self.mca1.spectrum.subscribe( + self._update_preview, run=False, run_kwargs={"event_type": "value"} + ) + + def _update_preview(self, old_value, value, **kwargs) -> None: + """Update the preview signal with the latest spectrum data.""" + logger.info(f"Received new spectrum data: {value}") + self.preview.put(value) + + def on_stage(self) -> DeviceStatus | StatusBase | None: + """ + Called while staging the device. + + Information about the upcoming scan can be accessed from the scan_info (self.scan_info.msg) object. + """ + self.collect_mode.set(0).wait() + self.preset_real_time.set(0).wait() + self.stop_all.put(1) + CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) + + def on_unstage(self) -> DeviceStatus | StatusBase | None: + """Called while unstaging the device.""" + self.stop_all.put(1) + self.erase_all.put(1) + CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) + + def on_pre_scan(self) -> DeviceStatus | StatusBase | None: + """Called right before the scan starts on all devices automatically.""" + + def on_trigger(self) -> DeviceStatus | StatusBase | None: + """Called when the device is triggered.""" + + def on_complete(self) -> DeviceStatus | StatusBase | None: + """Called to inquire if a device has completed a scans.""" + + def on_kickoff(self) -> DeviceStatus | StatusBase | 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.stop_all.put(1) + + def send_data(self): + """ + Wait until the Falcon is done acquiring data. + + This method blocks until the acquiring status is DONE. + """ + CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) + data = { + self.mca1.rois.roi0.count.name: { + "value": self.mca1.rois.roi0.count.get(), + "timestamp": time.time(), + }, + self.mca1.rois.elapsed_real_time.name: { + "value": self.mca1.elapsed_real_time.get(), + "timestamp": time.time(), + }, + self.dxp1.input_count_rate.name: { + "value": self.dxp1.input_count_rate.get(), + "timestamp": time.time(), + }, + self.dxp1.output_count_rate.name: { + "value": self.dxp1.output_count_rate.get(), + "timestamp": time.time(), + }, + self.dead_time_cor_cnts1.name: { + "value": self.dead_time_cor_cnts1.get(), + "timestamp": time.time(), + }, + } + self.async_data.put(data) diff --git a/superxas_bec/devices/trigger.py b/superxas_bec/devices/trigger.py index 77af731..6175639 100644 --- a/superxas_bec/devices/trigger.py +++ b/superxas_bec/devices/trigger.py @@ -1,18 +1,18 @@ """SuperXAS Trigger Device""" +import enum +import time + +from bec_lib.devicemanager import ScanInfo from bec_lib.logger import bec_logger from ophyd import Component as Cpt from ophyd import Device, DeviceStatus, EpicsSignal, EpicsSignalRO, Kind, StatusBase - -logger = bec_logger.logger - -import enum - -from bec_lib.devicemanager import ScanInfo from ophyd_devices import CompareStatus, TransitionStatus from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase -from superxas_bec.devices.falcon import Falcon, FalconAcquiringStatus +from superxas_bec.devices.falcon_direct import FalconAcquiringStatus, FalconSuperXASDirect + +logger = bec_logger.logger class ContinuousSamplingMode(int, enum.Enum): @@ -117,21 +117,37 @@ class Trigger(PSIDeviceBase, TriggerControl): and then wait for the Falcon to be done with acquiring. This is to ensure that data of PVs is updated and the sampling is done before data is being read from the device. """ + time_started = time.time() + logger.info(f"Triggering device {self.name} at {time_started}") falcon = self.device_manager.devices.get("falcon", None) if falcon is not None: - falcon: Falcon + falcon: FalconSuperXASDirect + falcon.erase_start.put(1) + logger.info( + f"Erase and start acquiring for {falcon.name} at {time.time() - time_started}" + ) # pylint: disable=protected-access CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING).wait( timeout=self._falcon_ready_timeout ) status_smpl = TransitionStatus(self.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE]) + logger.info(f"Triggering sampling for {self.name} at {time.time() - time_started}") self.smpl.put(1) - # We also need to wait for sampling to be done for the falcon if it's present. - # if falcon is not None: - # status_done = falcon.done_with_acquiring() - # status_done.wait(timeout=self._falcon_ready_timeout) + status_smpl.wait(timeout=self._pv_timeout) + logger.info(f"Sampling done for {self.name} at {time.time() - time_started}") + time.sleep(0.4) + if falcon is not None: + falcon.stop_all.put(1) + logger.info( + f"Waiting for Falcon to be ready after sampling at {time.time() - time_started}" + ) + # Wait for the Falcon to be done with acquiring data + CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE).wait( + timeout=self._falcon_ready_timeout + ) + falcon.send_data() return status_smpl def on_complete(self) -> DeviceStatus | StatusBase | None: -- 2.49.1 From 4f9e61cf367779eb27dc6df4d7e0296db6283fcd Mon Sep 17 00:00:00 2001 From: appel_c Date: Wed, 18 Jun 2025 08:51:28 +0200 Subject: [PATCH 05/42] quickfix wip falcon direct --- superxas_bec/devices/falcon_direct.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py index e7522ce..705f731 100644 --- a/superxas_bec/devices/falcon_direct.py +++ b/superxas_bec/devices/falcon_direct.py @@ -190,9 +190,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): Called after the device is connected and its signals are connected. Default values for signals should be set here. """ - self.mca1.spectrum.subscribe( - self._update_preview, run=False, run_kwargs={"event_type": "value"} - ) + self.mca1.spectrum.subscribe(self._update_preview, run=False) def _update_preview(self, old_value, value, **kwargs) -> None: """Update the preview signal with the latest spectrum data.""" -- 2.49.1 From 294504d00173967d7b3274902efba0b3096932b3 Mon Sep 17 00:00:00 2001 From: appel_c Date: Wed, 18 Jun 2025 08:54:21 +0200 Subject: [PATCH 06/42] wip --- superxas_bec/devices/falcon_direct.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py index 705f731..57af5a8 100644 --- a/superxas_bec/devices/falcon_direct.py +++ b/superxas_bec/devices/falcon_direct.py @@ -1,6 +1,6 @@ import enum +import time from collections import OrderedDict -from time import time from typing import TYPE_CHECKING import numpy as np -- 2.49.1 From 01cbc7f13eba2dd37ba870dda3bbd0e577fabfd0 Mon Sep 17 00:00:00 2001 From: appel_c Date: Wed, 18 Jun 2025 08:56:25 +0200 Subject: [PATCH 07/42] wip typo --- superxas_bec/devices/falcon_direct.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py index 57af5a8..e340995 100644 --- a/superxas_bec/devices/falcon_direct.py +++ b/superxas_bec/devices/falcon_direct.py @@ -242,7 +242,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): "value": self.mca1.rois.roi0.count.get(), "timestamp": time.time(), }, - self.mca1.rois.elapsed_real_time.name: { + self.mca1.elapsed_real_time.name: { "value": self.mca1.elapsed_real_time.get(), "timestamp": time.time(), }, -- 2.49.1 From bf02475d5ec3bc2b2691decfd7dd11c74fd19a63 Mon Sep 17 00:00:00 2001 From: appel_c Date: Wed, 18 Jun 2025 09:02:29 +0200 Subject: [PATCH 08/42] fix async send data --- superxas_bec/devices/falcon_direct.py | 89 +++++++++++++++++++-------- 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py index e340995..d9a35f6 100644 --- a/superxas_bec/devices/falcon_direct.py +++ b/superxas_bec/devices/falcon_direct.py @@ -150,8 +150,28 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): """Falcon implementierung at SuperXAS. prefix: 'X10DA-SITORO:'""" preview = Cpt(PreviewSignal, name="preview", ndim=1, doc="Preview signal for Falcon detector") - async_data = Cpt( - AsyncSignal, ndim=0, max_size=int(1e6), doc="Asynchronous data signal for Falcon detector" + icr = Cpt( + AsyncSignal, name="icr", ndim=0, max_size=int(1e6), doc="Async input count rate signal" + ) + ocr = Cpt( + AsyncSignal, name="ocr", ndim=0, max_size=int(1e6), doc="Async output count rate signal" + ) + elap_real_time = Cpt( + AsyncSignal, + name="elap_real_time", + ndim=0, + max_size=int(1e6), + doc="Async elapsed real time signal", + ) + roi0_count = Cpt( + AsyncSignal, name="roi0_count", ndim=0, max_size=int(1e6), doc="Async ROI 0 count signal" + ) + dead_cor_roi0_count = Cpt( + AsyncSignal, + name="dead_cor_roi0_count", + ndim=0, + max_size=int(1e6), + doc="Async dead time corrected ROI 0 count signal", ) ######################################## @@ -237,26 +257,45 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): This method blocks until the acquiring status is DONE. """ CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) - data = { - self.mca1.rois.roi0.count.name: { - "value": self.mca1.rois.roi0.count.get(), - "timestamp": time.time(), - }, - self.mca1.elapsed_real_time.name: { - "value": self.mca1.elapsed_real_time.get(), - "timestamp": time.time(), - }, - self.dxp1.input_count_rate.name: { - "value": self.dxp1.input_count_rate.get(), - "timestamp": time.time(), - }, - self.dxp1.output_count_rate.name: { - "value": self.dxp1.output_count_rate.get(), - "timestamp": time.time(), - }, - self.dead_time_cor_cnts1.name: { - "value": self.dead_time_cor_cnts1.get(), - "timestamp": time.time(), - }, - } - self.async_data.put(data) + logger.info(f"Sending data for {self.name} at {time.time()}") + self.icr.put( + { + self.dxp1.input_count_rate.name: { + "value": self.dxp1.input_count_rate.get(), + "timestamp": time.time(), + } + } + ) + self.ocr.put( + { + self.dxp1.output_count_rate.name: { + "value": self.dxp1.output_count_rate.get(), + "timestamp": time.time(), + } + } + ) + self.elap_real_time.put( + { + self.mca1.elapsed_real_time.name: { + "value": self.mca1.elapsed_real_time.get(), + "timestamp": time.time(), + } + } + ) + self.roi0_count.put( + { + self.mca1.rois.roi0.count.name: { + "value": self.mca1.rois.roi0.count.get(), + "timestamp": time.time(), + } + } + ) + self.dead_cor_roi0_count.put( + { + self.dead_time_cor_cnts1.name: { + "value": self.dead_time_cor_cnts1.get(), + "timestamp": time.time(), + } + } + ) + logger.info(f"Data sent for {self.name} at {time.time()}") -- 2.49.1 From 753b24a1fe50afd5d871c7a0fb076a631d9d0b53 Mon Sep 17 00:00:00 2001 From: appel_c Date: Wed, 18 Jun 2025 09:04:57 +0200 Subject: [PATCH 09/42] wip dta async --- superxas_bec/devices/falcon_direct.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py index d9a35f6..75b4df8 100644 --- a/superxas_bec/devices/falcon_direct.py +++ b/superxas_bec/devices/falcon_direct.py @@ -259,24 +259,14 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) logger.info(f"Sending data for {self.name} at {time.time()}") self.icr.put( - { - self.dxp1.input_count_rate.name: { - "value": self.dxp1.input_count_rate.get(), - "timestamp": time.time(), - } - } + {self.icr.name: {"value": self.dxp1.input_count_rate.get(), "timestamp": time.time()}} ) self.ocr.put( - { - self.dxp1.output_count_rate.name: { - "value": self.dxp1.output_count_rate.get(), - "timestamp": time.time(), - } - } + {self.ocr.name: {"value": self.dxp1.output_count_rate.get(), "timestamp": time.time()}} ) self.elap_real_time.put( { - self.mca1.elapsed_real_time.name: { + self.elap_real_time.name: { "value": self.mca1.elapsed_real_time.get(), "timestamp": time.time(), } @@ -284,7 +274,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): ) self.roi0_count.put( { - self.mca1.rois.roi0.count.name: { + self.roi0_count.name: { "value": self.mca1.rois.roi0.count.get(), "timestamp": time.time(), } @@ -292,7 +282,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): ) self.dead_cor_roi0_count.put( { - self.dead_time_cor_cnts1.name: { + self.dead_cor_roi0_count.name: { "value": self.dead_time_cor_cnts1.get(), "timestamp": time.time(), } -- 2.49.1 From c0fc133cf0096e8666aae413f9cb5fe52b428393 Mon Sep 17 00:00:00 2001 From: appel_c Date: Wed, 18 Jun 2025 09:09:59 +0200 Subject: [PATCH 10/42] wip test --- superxas_bec/devices/falcon_direct.py | 7 ++++--- superxas_bec/devices/trigger.py | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py index 75b4df8..8f34f8d 100644 --- a/superxas_bec/devices/falcon_direct.py +++ b/superxas_bec/devices/falcon_direct.py @@ -256,8 +256,9 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): This method blocks until the acquiring status is DONE. """ - CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) - logger.info(f"Sending data for {self.name} at {time.time()}") + time_started = time.time() + # CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) + logger.info(f"Sending data for {self.name} at {time_started}") self.icr.put( {self.icr.name: {"value": self.dxp1.input_count_rate.get(), "timestamp": time.time()}} ) @@ -288,4 +289,4 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): } } ) - logger.info(f"Data sent for {self.name} at {time.time()}") + logger.info(f"Data sent for {self.name} at {time.time()- time_started}") diff --git a/superxas_bec/devices/trigger.py b/superxas_bec/devices/trigger.py index 6175639..6950177 100644 --- a/superxas_bec/devices/trigger.py +++ b/superxas_bec/devices/trigger.py @@ -124,6 +124,7 @@ class Trigger(PSIDeviceBase, TriggerControl): if falcon is not None: falcon: FalconSuperXASDirect falcon.erase_start.put(1) + time.sleep(0.4) # Wait for erase to complete logger.info( f"Erase and start acquiring for {falcon.name} at {time.time() - time_started}" ) @@ -139,14 +140,14 @@ class Trigger(PSIDeviceBase, TriggerControl): logger.info(f"Sampling done for {self.name} at {time.time() - time_started}") time.sleep(0.4) if falcon is not None: - falcon.stop_all.put(1) + # falcon.stop_all.put(1) logger.info( f"Waiting for Falcon to be ready after sampling at {time.time() - time_started}" ) # Wait for the Falcon to be done with acquiring data - CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE).wait( - timeout=self._falcon_ready_timeout - ) + # CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE).wait( + # timeout=self._falcon_ready_timeout + # ) falcon.send_data() return status_smpl -- 2.49.1 From 695874bc417ef9f7d5fcf675d5825be0acdbc557 Mon Sep 17 00:00:00 2001 From: appel_c Date: Wed, 18 Jun 2025 09:12:55 +0200 Subject: [PATCH 11/42] debug trigger wait --- superxas_bec/devices/trigger.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/superxas_bec/devices/trigger.py b/superxas_bec/devices/trigger.py index 6950177..834dc0b 100644 --- a/superxas_bec/devices/trigger.py +++ b/superxas_bec/devices/trigger.py @@ -128,10 +128,18 @@ class Trigger(PSIDeviceBase, TriggerControl): logger.info( f"Erase and start acquiring for {falcon.name} at {time.time() - time_started}" ) - # pylint: disable=protected-access - CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING).wait( - timeout=self._falcon_ready_timeout - ) + try: + # pylint: disable=protected-access + CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING).wait( + timeout=self._falcon_ready_timeout + ) + except Exception as exc: + logger.error( + f"Falcon did not start acquiring. Current state {falcon.acquiring.get()}. " + ) + raise TimeoutError( + f"Falcon did not start acquiring within {self._falcon_ready_timeout} seconds." + ) from exc status_smpl = TransitionStatus(self.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE]) logger.info(f"Triggering sampling for {self.name} at {time.time() - time_started}") -- 2.49.1 From beb02444db96e68a1b7599b506f12ab20d66b091 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Wed, 18 Jun 2025 10:38:42 +0200 Subject: [PATCH 12/42] wip fix direct implementation at the beamline --- .../device_configs/x10da_config_250616.yaml | 4 +- .../x10da_config_falcon_test.yaml | 54 +++++++++++++++++++ superxas_bec/devices/falcon_direct.py | 38 ++++++++++--- 3 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 superxas_bec/device_configs/x10da_config_falcon_test.yaml diff --git a/superxas_bec/device_configs/x10da_config_250616.yaml b/superxas_bec/device_configs/x10da_config_250616.yaml index 2e174b7..5a98254 100644 --- a/superxas_bec/device_configs/x10da_config_250616.yaml +++ b/superxas_bec/device_configs/x10da_config_250616.yaml @@ -146,12 +146,12 @@ cm_yaw: softwareTrigger: false falcon: description: Falcon Sitoro detector - deviceClass: superxas_bec.devices.falcon.FalconSuperXAS + deviceClass: superxas_bec.devices.falcon_direct.FalconSuperXASDirect deviceConfig: prefix: 'X10DA-SITORO:' enabled: true onFailure: raise - readoutPriority: monitored + readoutPriority: async softwareTrigger: false filter_fe: description: Front End Filter diff --git a/superxas_bec/device_configs/x10da_config_falcon_test.yaml b/superxas_bec/device_configs/x10da_config_falcon_test.yaml new file mode 100644 index 0000000..332447f --- /dev/null +++ b/superxas_bec/device_configs/x10da_config_falcon_test.yaml @@ -0,0 +1,54 @@ +falcon: + description: Falcon Sitoro detector + deviceClass: superxas_bec.devices.falcon_direct.FalconSuperXASDirect + deviceConfig: + prefix: 'X10DA-SITORO:' + enabled: true + onFailure: raise + readoutPriority: async + softwareTrigger: false +manip_new_trx: + description: Old Sample Manipulator X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MAN:TRX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_new_try: + description: Old Sample Manipulator Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MAN:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_new_trz: + description: Old Sample Manipulator Z - Along beam + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MAN:TRZ + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +mono_energy: + description: Mono Energy + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-MO12-QEXAFS:E_TEST + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +trigger: + description: Trigger Card + deviceClass: superxas_bec.devices.trigger.Trigger + deviceConfig: + prefix: 'X10DA-ES1:' + enabled: true + onFailure: raise + readoutPriority: baseline + softwareTrigger: true diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py index 8f34f8d..ad5c5a1 100644 --- a/superxas_bec/devices/falcon_direct.py +++ b/superxas_bec/devices/falcon_direct.py @@ -5,7 +5,10 @@ from typing import TYPE_CHECKING import numpy as np from bec_lib.devicemanager import ScanInfo +from bec_server.device_server.devices.devicemanager import DeviceManagerDS from bec_lib.logger import bec_logger +from bec_lib.endpoints import MessageEndpoints +from bec_lib.messages import DeviceMessage from ophyd import Component as Cpt from ophyd import Device from ophyd import DynamicDeviceComponent as DDC @@ -149,7 +152,7 @@ class FalconControlDirect(Device): class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): """Falcon implementierung at SuperXAS. prefix: 'X10DA-SITORO:'""" - preview = Cpt(PreviewSignal, name="preview", ndim=1, doc="Preview signal for Falcon detector") + preview = Cpt(Signal, name="preview", kind=Kind.omitted, doc="Preview signal for Falcon detector") icr = Cpt( AsyncSignal, name="icr", ndim=0, max_size=int(1e6), doc="Async input count rate signal" ) @@ -178,7 +181,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): # Beamline Specific Implementations # ######################################## - def __init__(self, name: str, prefix: str = "", scan_info: ScanInfo | None = None, **kwargs): + def __init__(self, name: str, prefix: str = "", scan_info: ScanInfo | None = None, device_manager:DeviceManagerDS|None = None, **kwargs): """ Initialize Falcon device. @@ -188,7 +191,8 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): scan_info (ScanInfo): Information about the scan **kwargs: Additional keyword arguments """ - super().__init__(name=name, prefix=prefix, scan_info=scan_info, **kwargs) + super().__init__(name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager,**kwargs) + self.dm = device_manager self._pv_timeout = 1 self._async_read_data = [ "dxp_input_count_rate", @@ -210,12 +214,13 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): Called after the device is connected and its signals are connected. Default values for signals should be set here. """ - self.mca1.spectrum.subscribe(self._update_preview, run=False) + # self.mca1.spectrum.subscribe(self._update_preview, run=False) - def _update_preview(self, old_value, value, **kwargs) -> None: - """Update the preview signal with the latest spectrum data.""" - logger.info(f"Received new spectrum data: {value}") - self.preview.put(value) +#TODO add again once PreviewSIgnal works with GUI + # def _update_preview(self, old_value, value, **kwargs) -> None: + # """Update the preview signal with the latest spectrum data.""" + # logger.info(f"Received new spectrum data: {value}") + # self.preview.put(value) def on_stage(self) -> DeviceStatus | StatusBase | None: """ @@ -289,4 +294,21 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): } } ) + self._send_preview_async() logger.info(f"Data sent for {self.name} at {time.time()- time_started}") + + def _send_preview_async(self)->None: + metadata = self.scan_info.msg.metadata + metadata.update({"async_update": {"type" : "add", "max_shape" : None}}) + data = {self.preview.name : {"value" : self.mca1.spectrum.get(), "timestamp" : time.time()}} + msg = DeviceMessage( + signals=data, metadata=metadata + ) + self.dm.connector.xadd( + MessageEndpoints.device_async_readback( + scan_id=self.scan_info.msg.scan_id, device=self.name + ), + msg_dict={"data": msg}, + max_size=10000, + expire=900, + ) -- 2.49.1 From ad7a005763d333631dfecf12ba07bfb02e7c1840 Mon Sep 17 00:00:00 2001 From: appel_c Date: Wed, 18 Jun 2025 10:44:50 +0200 Subject: [PATCH 13/42] wip simplify direct integration --- superxas_bec/devices/falcon_direct.py | 57 +++++++++++---------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py index ad5c5a1..3e9450a 100644 --- a/superxas_bec/devices/falcon_direct.py +++ b/superxas_bec/devices/falcon_direct.py @@ -5,10 +5,10 @@ from typing import TYPE_CHECKING import numpy as np from bec_lib.devicemanager import ScanInfo -from bec_server.device_server.devices.devicemanager import DeviceManagerDS -from bec_lib.logger import bec_logger from bec_lib.endpoints import MessageEndpoints +from bec_lib.logger import bec_logger from bec_lib.messages import DeviceMessage +from bec_server.device_server.devices.devicemanager import DeviceManagerDS from ophyd import Component as Cpt from ophyd import Device from ophyd import DynamicDeviceComponent as DDC @@ -41,32 +41,12 @@ class DXPControl(Device): elapsed_real_time = Cpt(EpicsSignalRO, "ElapsedRealTime", kind=Kind.omitted, auto_monitor=True) -class ROI(Device): - - # 'name' is not an allowed attribute - label = Cpt(EpicsSignal, "NM", lazy=True) - count = Cpt(EpicsSignalRO, "", lazy=True) - net_count = Cpt(EpicsSignalRO, "N", lazy=True) - - -def add_rois(range_, **kwargs): - defn = OrderedDict() - - for roi in range_: - if not (0 <= roi < 32): - raise ValueError("roi must be in the set [0,31]") - - attr = "roi{}".format(roi) - defn[attr] = (ROI, ".R{}".format(roi), kwargs) - - return defn - - class MCAControl(Device): """MCA Control Device for Falcon detector""" spectrum = Cpt(EpicsSignalRO, ".VAL", kind=Kind.omitted, auto_monitor=True) - rois = DDC(add_rois(range(0, 1), kind="omitted"), kind="omitted") + roi_count = Cpt(EpicsSignalRO, ".R0", kind=Kind.omitted, auto_monitor=True) + roi_label = Cpt(EpicsSignal, ".R0NM", kind=Kind.omitted, auto_monitor=True) elapsed_real_time = Cpt(EpicsSignalRO, ".ERTM", kind=Kind.omitted, auto_monitor=True) @@ -152,7 +132,9 @@ class FalconControlDirect(Device): class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): """Falcon implementierung at SuperXAS. prefix: 'X10DA-SITORO:'""" - preview = Cpt(Signal, name="preview", kind=Kind.omitted, doc="Preview signal for Falcon detector") + preview = Cpt( + Signal, name="preview", kind=Kind.omitted, doc="Preview signal for Falcon detector" + ) icr = Cpt( AsyncSignal, name="icr", ndim=0, max_size=int(1e6), doc="Async input count rate signal" ) @@ -181,7 +163,14 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): # Beamline Specific Implementations # ######################################## - def __init__(self, name: str, prefix: str = "", scan_info: ScanInfo | None = None, device_manager:DeviceManagerDS|None = None, **kwargs): + def __init__( + self, + name: str, + prefix: str = "", + scan_info: ScanInfo | None = None, + device_manager: DeviceManagerDS | None = None, + **kwargs, + ): """ Initialize Falcon device. @@ -191,7 +180,9 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): scan_info (ScanInfo): Information about the scan **kwargs: Additional keyword arguments """ - super().__init__(name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager,**kwargs) + super().__init__( + name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs + ) self.dm = device_manager self._pv_timeout = 1 self._async_read_data = [ @@ -216,7 +207,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): """ # self.mca1.spectrum.subscribe(self._update_preview, run=False) -#TODO add again once PreviewSIgnal works with GUI + # TODO add again once PreviewSIgnal works with GUI # def _update_preview(self, old_value, value, **kwargs) -> None: # """Update the preview signal with the latest spectrum data.""" # logger.info(f"Received new spectrum data: {value}") @@ -297,13 +288,11 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): self._send_preview_async() logger.info(f"Data sent for {self.name} at {time.time()- time_started}") - def _send_preview_async(self)->None: + def _send_preview_async(self) -> None: metadata = self.scan_info.msg.metadata - metadata.update({"async_update": {"type" : "add", "max_shape" : None}}) - data = {self.preview.name : {"value" : self.mca1.spectrum.get(), "timestamp" : time.time()}} - msg = DeviceMessage( - signals=data, metadata=metadata - ) + metadata.update({"async_update": {"type": "add", "max_shape": None}}) + data = {self.preview.name: {"value": self.mca1.spectrum.get(), "timestamp": time.time()}} + msg = DeviceMessage(signals=data, metadata=metadata) self.dm.connector.xadd( MessageEndpoints.device_async_readback( scan_id=self.scan_info.msg.scan_id, device=self.name -- 2.49.1 From 7ef1ac405cca5d0dd7a769585541a109d1eb0bd8 Mon Sep 17 00:00:00 2001 From: appel_c Date: Wed, 18 Jun 2025 10:52:58 +0200 Subject: [PATCH 14/42] fix metadata update --- superxas_bec/devices/falcon_direct.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py index 3e9450a..bda5a73 100644 --- a/superxas_bec/devices/falcon_direct.py +++ b/superxas_bec/devices/falcon_direct.py @@ -290,7 +290,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): def _send_preview_async(self) -> None: metadata = self.scan_info.msg.metadata - metadata.update({"async_update": {"type": "add", "max_shape": None}}) + metadata.update({"async_update": {"type": "add", "max_shape": [None]}}) data = {self.preview.name: {"value": self.mca1.spectrum.get(), "timestamp": time.time()}} msg = DeviceMessage(signals=data, metadata=metadata) self.dm.connector.xadd( -- 2.49.1 From 48986be97e48c2a8388a313a29efa3a4665873a9 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Wed, 18 Jun 2025 11:33:55 +0200 Subject: [PATCH 15/42] wip beamline refactoring --- .../x10da_config_falcon_test.yaml | 2 +- superxas_bec/devices/falcon_direct.py | 86 ++++++------------- 2 files changed, 26 insertions(+), 62 deletions(-) diff --git a/superxas_bec/device_configs/x10da_config_falcon_test.yaml b/superxas_bec/device_configs/x10da_config_falcon_test.yaml index 332447f..4497dc1 100644 --- a/superxas_bec/device_configs/x10da_config_falcon_test.yaml +++ b/superxas_bec/device_configs/x10da_config_falcon_test.yaml @@ -5,7 +5,7 @@ falcon: prefix: 'X10DA-SITORO:' enabled: true onFailure: raise - readoutPriority: async + readoutPriority: monitored softwareTrigger: false manip_new_trx: description: Old Sample Manipulator X-Translation diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py index bda5a73..d86dc2b 100644 --- a/superxas_bec/devices/falcon_direct.py +++ b/superxas_bec/devices/falcon_direct.py @@ -38,15 +38,15 @@ class DXPControl(Device): input_count_rate = Cpt(EpicsSignalRO, "InputCountRate", kind=Kind.omitted, auto_monitor=True) output_count_rate = Cpt(EpicsSignalRO, "OutputCountRate", kind=Kind.omitted, auto_monitor=True) - elapsed_real_time = Cpt(EpicsSignalRO, "ElapsedRealTime", kind=Kind.omitted, auto_monitor=True) + elapsed_real_time = Cpt(EpicsSignalRO, "ElapsedRealTime", kind=Kind.omitted) class MCAControl(Device): """MCA Control Device for Falcon detector""" - spectrum = Cpt(EpicsSignalRO, ".VAL", kind=Kind.omitted, auto_monitor=True) + spectrum = Cpt(EpicsSignalRO, ".VAL", kind=Kind.omitted) roi_count = Cpt(EpicsSignalRO, ".R0", kind=Kind.omitted, auto_monitor=True) - roi_label = Cpt(EpicsSignal, ".R0NM", kind=Kind.omitted, auto_monitor=True) + roi_label = Cpt(EpicsSignal, ".R0NM", kind=Kind.omitted) elapsed_real_time = Cpt(EpicsSignalRO, ".ERTM", kind=Kind.omitted, auto_monitor=True) @@ -73,7 +73,7 @@ class DeadTimeCorrectedCounts(Signal): icr = dxp.input_count_rate.get() ocr = dxp.output_count_rate.get() - roi = mca.rois.roi0.count.get() + roi = mca.roi_count.get() ert = mca.elapsed_real_time.get() print(icr, ocr, roi, ert) @@ -115,16 +115,16 @@ class FalconControlDirect(Device): acquiring = Cpt(EpicsSignal, "Acquiring", kind=Kind.omitted, auto_monitor=True) # DXP parameters - dxp1 = Cpt(DXPControl, "dxp1:") + dxp1 = Cpt(DXPControl, "dxp1:", kind=Kind.omitted) # dxp2 = Cpt(EpicsDXPFalcon, "dxp2:") # MCA record with spectrum data - mca1 = Cpt(MCAControl, "mca1") + mca1 = Cpt(MCAControl, "mca1", kind=Kind.omitted) # mca2 = Cpt(EpicsMCARecord, "mca2") # Norm Signal dead_time_cor_cnts1 = Cpt( - DeadTimeCorrectedCounts, name="dead_time_cor_cnts", channel=1, kind=Kind.hinted + DeadTimeCorrectedCounts, name="dead_time_cor_cnts", channel=1, kind=Kind.omitted ) # dead_time_cor_cnts2 = Cpt(DeadTimeCorrectedCounts, name='dead_time_cor_cnts', channel=2, kind=Kind.normal) @@ -133,30 +133,18 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): """Falcon implementierung at SuperXAS. prefix: 'X10DA-SITORO:'""" preview = Cpt( - Signal, name="preview", kind=Kind.omitted, doc="Preview signal for Falcon detector" + Signal, name="preview", kind=Kind.normal, doc="Preview signal for Falcon detector" ) icr = Cpt( - AsyncSignal, name="icr", ndim=0, max_size=int(1e6), doc="Async input count rate signal" - ) - ocr = Cpt( - AsyncSignal, name="ocr", ndim=0, max_size=int(1e6), doc="Async output count rate signal" - ) - elap_real_time = Cpt( - AsyncSignal, - name="elap_real_time", - ndim=0, - max_size=int(1e6), - doc="Async elapsed real time signal", - ) - roi0_count = Cpt( - AsyncSignal, name="roi0_count", ndim=0, max_size=int(1e6), doc="Async ROI 0 count signal" - ) + Signal, name="icr", kind=Kind.normal) + ocr = Cpt(Signal, name="icr", kind=Kind.normal) + elap_real_time = Cpt(Signal, name="icr", kind=Kind.normal) + roi0_count = Cpt(Signal, name="icr", kind=Kind.normal) dead_cor_roi0_count = Cpt( - AsyncSignal, + Signal, name="dead_cor_roi0_count", - ndim=0, - max_size=int(1e6), doc="Async dead time corrected ROI 0 count signal", + kind=Kind.normal ) ######################################## @@ -205,6 +193,8 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): Called after the device is connected and its signals are connected. Default values for signals should be set here. """ + self.stop_all.put(1) + CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(5) # self.mca1.spectrum.subscribe(self._update_preview, run=False) # TODO add again once PreviewSIgnal works with GUI @@ -219,16 +209,14 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): Information about the upcoming scan can be accessed from the scan_info (self.scan_info.msg) object. """ + self.stop_all.put(1, use_complete=True) + CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) self.collect_mode.set(0).wait() self.preset_real_time.set(0).wait() - self.stop_all.put(1) - CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) def on_unstage(self) -> DeviceStatus | StatusBase | None: """Called while unstaging the device.""" self.stop_all.put(1) - self.erase_all.put(1) - CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) def on_pre_scan(self) -> DeviceStatus | StatusBase | None: """Called right before the scan starts on all devices automatically.""" @@ -255,37 +243,13 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): time_started = time.time() # CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) logger.info(f"Sending data for {self.name} at {time_started}") - self.icr.put( - {self.icr.name: {"value": self.dxp1.input_count_rate.get(), "timestamp": time.time()}} - ) - self.ocr.put( - {self.ocr.name: {"value": self.dxp1.output_count_rate.get(), "timestamp": time.time()}} - ) - self.elap_real_time.put( - { - self.elap_real_time.name: { - "value": self.mca1.elapsed_real_time.get(), - "timestamp": time.time(), - } - } - ) - self.roi0_count.put( - { - self.roi0_count.name: { - "value": self.mca1.rois.roi0.count.get(), - "timestamp": time.time(), - } - } - ) - self.dead_cor_roi0_count.put( - { - self.dead_cor_roi0_count.name: { - "value": self.dead_time_cor_cnts1.get(), - "timestamp": time.time(), - } - } - ) - self._send_preview_async() + self.icr.put(self.dxp1.input_count_rate.get()) + self.ocr.put(self.dxp1.output_count_rate.get()) + self.elap_real_time.put(self.mca1.elapsed_real_time.get()) + self.roi0_count.put(self.mca1.roi_count.get()) + self.dead_cor_roi0_count.put(self.dead_time_cor_cnts1.get()) + self.preview.put(self.mca1.spectrum.get()) + # self._send_preview_async() logger.info(f"Data sent for {self.name} at {time.time()- time_started}") def _send_preview_async(self) -> None: -- 2.49.1 From b9623ecab2bd059bf8a229155de6127c622db3a3 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Thu, 19 Jun 2025 11:43:36 +0200 Subject: [PATCH 16/42] wip fixes at the beamline --- .../x10da_config_falcon_test.yaml | 2 +- superxas_bec/devices/falcon_direct.py | 35 ++++++++++++------- superxas_bec/devices/trigger.py | 12 ++++--- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/superxas_bec/device_configs/x10da_config_falcon_test.yaml b/superxas_bec/device_configs/x10da_config_falcon_test.yaml index 4497dc1..332447f 100644 --- a/superxas_bec/device_configs/x10da_config_falcon_test.yaml +++ b/superxas_bec/device_configs/x10da_config_falcon_test.yaml @@ -5,7 +5,7 @@ falcon: prefix: 'X10DA-SITORO:' enabled: true onFailure: raise - readoutPriority: monitored + readoutPriority: async softwareTrigger: false manip_new_trx: description: Old Sample Manipulator X-Translation diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py index d86dc2b..eee108e 100644 --- a/superxas_bec/devices/falcon_direct.py +++ b/superxas_bec/devices/falcon_direct.py @@ -44,7 +44,7 @@ class DXPControl(Device): class MCAControl(Device): """MCA Control Device for Falcon detector""" - spectrum = Cpt(EpicsSignalRO, ".VAL", kind=Kind.omitted) + spectrum = Cpt(EpicsSignalRO, ".VAL", kind=Kind.omitted, auto_monitor=True) roi_count = Cpt(EpicsSignalRO, ".R0", kind=Kind.omitted, auto_monitor=True) roi_label = Cpt(EpicsSignal, ".R0NM", kind=Kind.omitted) elapsed_real_time = Cpt(EpicsSignalRO, ".ERTM", kind=Kind.omitted, auto_monitor=True) @@ -75,7 +75,7 @@ class DeadTimeCorrectedCounts(Signal): ocr = dxp.output_count_rate.get() roi = mca.roi_count.get() ert = mca.elapsed_real_time.get() - print(icr, ocr, roi, ert) + # print(icr, ocr, roi, ert) if icr == 0 or ocr == 0: return 0 @@ -133,7 +133,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): """Falcon implementierung at SuperXAS. prefix: 'X10DA-SITORO:'""" preview = Cpt( - Signal, name="preview", kind=Kind.normal, doc="Preview signal for Falcon detector" + Signal, name="preview", kind=Kind.omitted, doc="Preview signal for Falcon detector" ) icr = Cpt( Signal, name="icr", kind=Kind.normal) @@ -172,13 +172,14 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs ) self.dm = device_manager - self._pv_timeout = 1 + self._pv_timeout = 5 self._async_read_data = [ "dxp_input_count_rate", "dxp_output_count_rate", "mca_elapsed_real_time", "mca_rois_roi0_count", ] + self._index = 0 def on_init(self) -> None: """ @@ -193,8 +194,8 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): Called after the device is connected and its signals are connected. Default values for signals should be set here. """ - self.stop_all.put(1) - CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(5) + # self.stop_all.put(1) + # CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(5) # self.mca1.spectrum.subscribe(self._update_preview, run=False) # TODO add again once PreviewSIgnal works with GUI @@ -209,10 +210,18 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): Information about the upcoming scan can be accessed from the scan_info (self.scan_info.msg) object. """ - self.stop_all.put(1, use_complete=True) - CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) + # self.stop_all.put(1, use_complete=True) + if self.acquiring.get() == FalconAcquiringStatus.ACQUIRING: + status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) + self.stop_all.put(1) + try: + status.wait(self._pv_timeout) + except Exception as exc: + logger.warning(f"Device {self.name} failed to reach state 'done', current state {FalconAcquiringStatus(self.acquiring.get())}") + self.collect_mode.set(0).wait() self.preset_real_time.set(0).wait() + self._index = 0 def on_unstage(self) -> DeviceStatus | StatusBase | None: """Called while unstaging the device.""" @@ -244,17 +253,16 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): # CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) logger.info(f"Sending data for {self.name} at {time_started}") self.icr.put(self.dxp1.input_count_rate.get()) + logger.info(f"Data to plot {self.icr.get()}") self.ocr.put(self.dxp1.output_count_rate.get()) self.elap_real_time.put(self.mca1.elapsed_real_time.get()) self.roi0_count.put(self.mca1.roi_count.get()) self.dead_cor_roi0_count.put(self.dead_time_cor_cnts1.get()) - self.preview.put(self.mca1.spectrum.get()) - # self._send_preview_async() + self._send_preview_async() logger.info(f"Data sent for {self.name} at {time.time()- time_started}") def _send_preview_async(self) -> None: - metadata = self.scan_info.msg.metadata - metadata.update({"async_update": {"type": "add", "max_shape": [None]}}) + metadata = {"async_update": {"type": "add", "max_shape": [None, 3000]}} data = {self.preview.name: {"value": self.mca1.spectrum.get(), "timestamp": time.time()}} msg = DeviceMessage(signals=data, metadata=metadata) self.dm.connector.xadd( @@ -262,6 +270,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): scan_id=self.scan_info.msg.scan_id, device=self.name ), msg_dict={"data": msg}, - max_size=10000, + max_size=1000, expire=900, ) + self._index += 1 diff --git a/superxas_bec/devices/trigger.py b/superxas_bec/devices/trigger.py index 834dc0b..810246d 100644 --- a/superxas_bec/devices/trigger.py +++ b/superxas_bec/devices/trigger.py @@ -70,7 +70,7 @@ class Trigger(PSIDeviceBase, TriggerControl): ): super().__init__(name=name, prefix=prefix, scan_info=scan_info, **kwargs) self.device_manager = device_manager - self._pv_timeout = 1 + self._pv_timeout = 5 self._falcon_ready_timeout = 5 # seconds ######################################## @@ -124,6 +124,8 @@ class Trigger(PSIDeviceBase, TriggerControl): if falcon is not None: falcon: FalconSuperXASDirect falcon.erase_start.put(1) + # falcon.erase_all.put(1) + # falcon.start_all.put(1) time.sleep(0.4) # Wait for erase to complete logger.info( f"Erase and start acquiring for {falcon.name} at {time.time() - time_started}" @@ -131,14 +133,14 @@ class Trigger(PSIDeviceBase, TriggerControl): try: # pylint: disable=protected-access CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING).wait( - timeout=self._falcon_ready_timeout + timeout=self._pv_timeout ) except Exception as exc: logger.error( - f"Falcon did not start acquiring. Current state {falcon.acquiring.get()}. " + f"Falcon did not start acquiring. Current state {FalconAcquiringStatus(falcon.acquiring.get())}. " ) raise TimeoutError( - f"Falcon did not start acquiring within {self._falcon_ready_timeout} seconds." + f"Falcon did not start acquiring within {self._pv_timeout} seconds." ) from exc status_smpl = TransitionStatus(self.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE]) @@ -157,6 +159,7 @@ class Trigger(PSIDeviceBase, TriggerControl): # timeout=self._falcon_ready_timeout # ) falcon.send_data() + # falcon.stop_all.put(1) return status_smpl def on_complete(self) -> DeviceStatus | StatusBase | None: @@ -167,7 +170,6 @@ class Trigger(PSIDeviceBase, TriggerControl): def on_stop(self) -> None: """Called when the device is stopped.""" - self.task_handler.shutdown() def set_exposure_time(self, value: float) -> DeviceStatus: """Utility method to set exposure time complying to device logic with cycle of min 0.2s.""" -- 2.49.1 From db7a0b462b6e7567a89e9098f54b5fcf88d94fb9 Mon Sep 17 00:00:00 2001 From: appel_c Date: Thu, 19 Jun 2025 12:03:43 +0200 Subject: [PATCH 17/42] wip test script for falcon --- superxas_bec/devices/test.py | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 superxas_bec/devices/test.py diff --git a/superxas_bec/devices/test.py b/superxas_bec/devices/test.py new file mode 100644 index 0000000..67ff855 --- /dev/null +++ b/superxas_bec/devices/test.py @@ -0,0 +1,74 @@ +import time + +import numpy as np +from ophyd_devices import CompareStatus, TransitionStatus + +from superxas_bec.devices.falcon import FalconAcquiringStatus, FalconSuperXAS +from superxas_bec.devices.trigger import ContinuousSamplingMode, SamplingDone, Trigger + + +def mock_motor_move(pos: float) -> None: + """Mock function to simulate motor movement.""" + print(f"Mock Motor starts moving...") + time.sleep(0.5) + print(f"Mock Motor has reached the target position {pos}") + + +if __name__ == "__main__": + + # Exposure time 0.6s + exp_time = 0.6 + # steps = 10 + positions = np.linspace(0, 1, 10) # Simulated positions for the motor + + # Example usage of the FalconSuperXAS and Trigger classes + falcon = FalconSuperXAS(name="falcon", prefix="X10DA-SITORO:") + trigger = Trigger(name="trigger", prefix="X10DA-ES1:") + print(f"Initialized {falcon.name} with prefix {falcon.prefix}") + print(f"Initialized {trigger.name} with prefix {trigger.prefix}") + falcon.wait_for_connection(all_signals=True, timeout=60) + trigger.wait_for_connection(all_signals=True, timeout=60) + + # Simulate a scan! + for iteration in range(3): + print(f"\nStarting iteration {iteration + 1} of the scan...") + + #### STAGE #### + # Check if Falcon is acquiring data and stop if necessary + if falcon.acquiring.get() == FalconAcquiringStatus.ACQUIRING: + print("Falcon is currently acquiring data.") + print("Stopping all acquisitions...") + status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) + falcon.stop_all.put(1) + status.wait(timeout=5) + print("All acquisitions stopped.") + + # Prepare the devices + falcon.collect_mode.set(0).wait() + falcon.preset_real_time.set(0).wait() + print("Falcon is prepared for the scan.") + trigger.start_csmpl.set(ContinuousSamplingMode.OFF).wait(timeout=5) + cycles = max(int(exp_time * 5), 1) # Must be at least 1 cycle, each cycle is 0.2s + trigger.total_cycles.set(cycles).set(timeout=5) + print(f"Trigger is prepared for the scan with {cycles} cycles.") + #### End STAGE #### + + ### Trigger at each point### + # Simulate motion and data acquisition at each point + print("Starting the scan...") + for pos in positions: + mock_motor_move(pos) + # Trigger the devices + falcon.erase_start.put(1) + time.sleep(0.4) + status_smpl = TransitionStatus( + trigger.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE] + ) + trigger.smpl.put(1) + status_smpl.wait() + time.sleep(0.4) + status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) + falcon.stop_all.put(1) + status.wait(timeout=5) + time.sleep(0.5) # Simulate some processing time + print(falcon.mca1.rois.roi0.count.get()) -- 2.49.1 From 41d1101a83c595b31f88594e0e28ec7b45ba1a92 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Thu, 19 Jun 2025 17:22:01 +0200 Subject: [PATCH 18/42] wip falcon --- .../x10da_config_falcon_test.yaml | 2 +- superxas_bec/devices/falcon.py | 226 ++++++++++-------- superxas_bec/devices/test.py | 17 +- superxas_bec/devices/trigger.py | 58 ++--- 4 files changed, 160 insertions(+), 143 deletions(-) diff --git a/superxas_bec/device_configs/x10da_config_falcon_test.yaml b/superxas_bec/device_configs/x10da_config_falcon_test.yaml index 332447f..1118cc9 100644 --- a/superxas_bec/device_configs/x10da_config_falcon_test.yaml +++ b/superxas_bec/device_configs/x10da_config_falcon_test.yaml @@ -1,6 +1,6 @@ falcon: description: Falcon Sitoro detector - deviceClass: superxas_bec.devices.falcon_direct.FalconSuperXASDirect + deviceClass: superxas_bec.devices.falcon.FalconSuperXAS deviceConfig: prefix: 'X10DA-SITORO:' enabled: true diff --git a/superxas_bec/devices/falcon.py b/superxas_bec/devices/falcon.py index 1c12169..a7ac808 100644 --- a/superxas_bec/devices/falcon.py +++ b/superxas_bec/devices/falcon.py @@ -1,12 +1,15 @@ """FALCON device implementation for SuperXAS""" import enum - +import time import numpy as np from bec_lib.devicemanager import ScanInfo from bec_lib.logger import bec_logger +from bec_lib.messages import DeviceMessage +from bec_lib.endpoints import MessageEndpoints +from ophyd_devices import CompareStatus, TransitionStatus from ophyd import Component as Cpt -from ophyd import DeviceStatus, Kind, Signal, StatusBase +from ophyd import DeviceStatus, Kind, Signal, StatusBase, Staged from ophyd.status import SubscriptionStatus from ophyd_devices.devices.dxp import EpicsDXPFalcon, EpicsMCARecord, Falcon from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase @@ -20,77 +23,51 @@ class FalconAcquiringStatus(int, enum.Enum): DONE = 0 ACQUIRING = 1 +def compute_dead_time_corrected_signal(icr:float, ocr:float, roi:float, ert:float): + _dead_time = 1.182e-7 + if icr == 0 or ocr == 0: + return 0 -class DeadTimeCorrectedCounts(Signal): - """Signal to calculate dead time corrected counts""" - - def __init__(self, name: str, channel: int, **kwargs): - """ - Initialize DeadTimeCorrectedCounts signal. - - Args: - name (str): Name of the signal - channel (int): Channel number - """ - super().__init__(name=name, **kwargs) - self._channel = channel - self._dead_time = 1.182e-7 - - # pylint: disable=arguments-differ - def get(self) -> float: - """Get dead time corrected counts base on signals from dxp and mca of Falcon""" - dxp: EpicsDXPFalcon = getattr(self.parent, f"dxp{self._channel}") - mca: EpicsMCARecord = getattr(self.parent, f"mca{self._channel}") - - icr = dxp.input_count_rate.get() - ocr = dxp.output_count_rate.get() - roi = mca.rois.roi0.count.get() - ert = mca.elapsed_real_time.get() - print(icr, ocr, roi, ert) - - if icr == 0 or ocr == 0: + # Check that relative change is large enough + test = 1e9 + test_icr = icr + n = 0 + while test > _dead_time and n < 30: + try: + true_icr = icr * np.exp(test_icr * _dead_time) + test = (true_icr - test_icr) / test_icr + test_icr = true_icr + n += 1 + except Exception as e: # pylint: disable=broad-except + logger.info(f"Error in computation of deadtime corrected signal, error: {e}") return 0 - # Check that relative change is large enough - test = 1e9 - test_icr = icr - n = 0 - while test > self._dead_time and n < 30: - try: - true_icr = icr * np.exp(test_icr * self._dead_time) - test = (true_icr - test_icr) / test_icr - test_icr = true_icr - n += 1 - except Exception as e: # pylint: disable=broad-except - logger.info(f"Error in computation of signal {self.name}, error: {e}") - return 0 - - # Return corrected roi counts - cor_roi_cnts = 0 - if ocr * ert != 0: - cor_roi_cnts = roi * true_icr / (ocr * ert) - return cor_roi_cnts + # Return corrected roi counts + cor_roi_cnts = 0 + if ocr * ert != 0: + cor_roi_cnts = roi * true_icr / (ocr * ert) + return cor_roi_cnts class FalconControl(Falcon): """Falcon Control class at SuperXAS. prefix: 'X10DA-SITORO:'""" - _default_read_attrs = Falcon._default_read_attrs + ( - "dxp1", - # # "dxp2", - "mca1", - # # "mca2", - "dead_time_cor_cnts1", - # # "dead_time_cor_cnts2", - ) - _default_configuration_attrs = Falcon._default_configuration_attrs + ( - "dxp1", - # "dxp2", - "mca1", - # "mca2", - "dead_time_cor_cnts1", - # "dead_time_cor_cnts2", - ) + # _default_read_attrs = Falcon._default_read_attrs + ( + # "dxp1", + # # # "dxp2", + # "mca1", + # # # "mca2", + # "dead_time_cor_cnts1", + # # # "dead_time_cor_cnts2", + # ) + # _default_configuration_attrs = Falcon._default_configuration_attrs + ( + # "dxp1", + # # "dxp2", + # "mca1", + # # "mca2", + # "dead_time_cor_cnts1", + # # "dead_time_cor_cnts2", + # ) # DXP parameters dxp1 = Cpt(EpicsDXPFalcon, "dxp1:") @@ -100,21 +77,35 @@ class FalconControl(Falcon): mca1 = Cpt(EpicsMCARecord, "mca1") # mca2 = Cpt(EpicsMCARecord, "mca2") - # Norm Signal - dead_time_cor_cnts1 = Cpt( - DeadTimeCorrectedCounts, name="dead_time_cor_cnts", channel=1, kind=Kind.hinted - ) # dead_time_cor_cnts2 = Cpt(DeadTimeCorrectedCounts, name='dead_time_cor_cnts', channel=2, kind=Kind.normal) class FalconSuperXAS(PSIDeviceBase, FalconControl): """Falcon implementierung at SuperXAS. prefix: 'X10DA-SITORO:'""" + _default_read_attrs = ("icr", "ocr", "elap_real_time", "roi0_count", "dead_cor_roi0_count") + _default_configuration_attrs = None + + preview = Cpt( + Signal, name="preview", kind=Kind.omitted, doc="Preview signal for Falcon detector" + ) + icr = Cpt( + Signal, name="icr", kind=Kind.normal) + ocr = Cpt(Signal, name="icr", kind=Kind.normal) + elap_real_time = Cpt(Signal, name="icr", kind=Kind.normal) + roi0_count = Cpt(Signal, name="icr", kind=Kind.normal) + dead_cor_roi0_count = Cpt( + Signal, + name="dead_cor_roi0_count", + doc="Async dead time corrected ROI 0 count signal", + kind=Kind.normal + ) + ######################################## # Beamline Specific Implementations # ######################################## - def __init__(self, name: str, prefix: str = "", scan_info: ScanInfo | None = None, **kwargs): + def __init__(self, name: str, prefix: str = "", scan_info: ScanInfo | None = None, device_manager=None, **kwargs): """ Initialize Falcon device. @@ -124,8 +115,10 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): scan_info (ScanInfo): Information about the scan **kwargs: Additional keyword arguments """ - super().__init__(name=name, prefix=prefix, scan_info=scan_info, **kwargs) - self._pv_timeout = 1 + super().__init__(name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs) + self.device_manager = device_manager + self.mca1.stage_sigs = {} + self._pv_timeout = 5 def on_init(self) -> None: """ @@ -141,34 +134,39 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): Default values for signals should be set here. """ + def stage(self) -> list[object] | DeviceStatus | StatusBase: # type: ignore + """Stage the device.""" + if self.staged != Staged.no: + return [self] + self.stopped = False + status = self.on_stage() # pylint: disable=assignment-from-no-return + if isinstance(status, StatusBase): + return status + return [self] + def on_stage(self) -> DeviceStatus | StatusBase | None: """ Called while staging the device. Information about the upcoming scan can be accessed from the scan_info (self.scan_info.msg) object. """ - self.collect_mode.set(0).wait() - self.preset_real_time.set(0).wait() - self.stop_all.put(1) - if ( - self.wait_for_condition( - lambda: self.acquiring.get() == FalconAcquiringStatus.DONE, timeout=self._pv_timeout - ) - is False - ): - raise TimeoutError("Timeout on Falcon stage") + if self.acquiring.get() != FalconAcquiringStatus.DONE: + logger.info(f"Falcon state was {self.acquiring.get()} during stage. Calling stop_all") + status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) + self.cancel_on_stop(status) + self.stop_all.put(1) + status.wait(timeout=self._pv_timeout) + + self.collect_mode.set(0).wait(timeout=self._pv_timeout) + self.preset_real_time.set(0).wait(timeout=self._pv_timeout) def on_unstage(self) -> DeviceStatus | StatusBase | None: """Called while unstaging the device.""" - self.stop_all.put(1) - self.erase_all.put(1) - if ( - self.wait_for_condition( - lambda: self.acquiring.get() == FalconAcquiringStatus.DONE, timeout=self._pv_timeout - ) - is False - ): - raise TimeoutError("Timeout on Falcon unstage") + if self.acquiring.get() != FalconAcquiringStatus.DONE: + self.stop_all.put(1) + status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) + self.cancel_on_stop(status) + return status def on_pre_scan(self) -> DeviceStatus | StatusBase | None: """Called right before the scan starts on all devices automatically.""" @@ -186,19 +184,37 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): """Called when the device is stopped.""" self.stop_all.put(1) - def _stop_erase_and_wait_for_acquiring(self) -> DeviceStatus: - """Method called from the Trigger card to reset counts on the Falcon""" + def send_data(self): + """ + Wait until the Falcon is done acquiring data. - if self.acquiring.get() != FalconAcquiringStatus.DONE: - self.stop_all.put(1) + This method blocks until the acquiring status is DONE. + """ + time_started = time.time() + # CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) + logger.info(f"Sending data for {self.name} at {time_started}") + icr = self.dxp1.input_count_rate.get() + ocr = self.dxp1.output_count_rate.get() + roi = self.mca1.elapsed_real_time.get() + ert = self.mca1.elapsed_real_time.get() + self.icr.put(icr) + logger.info(f"Data to plot {self.icr.get()}") + self.ocr.put(ocr) + self.elap_real_time.put(ert) + self.roi0_count.put(roi) + self.dead_cor_roi0_count.put(compute_dead_time_corrected_signal(icr,ocr,roi,ert)) + # self._send_preview_async() + logger.info(f"Data sent for {self.name} at {time.time()- time_started}") - def _check_acquiriting(*, old_value, value, **kwargs): - if old_value == FalconAcquiringStatus.DONE and value == FalconAcquiringStatus.ACQUIRING: - return True - return False - - status = SubscriptionStatus(self.acquiring, _check_acquiriting) - - logger.info("Triggering Falcon") - self.erase_start.put(1) - return status + def _send_preview_async(self) -> None: + metadata = {"async_update": {"type": "add", "max_shape": [None, 3000]}} + data = {self.preview.name: {"value": self.mca1.spectrum.get(), "timestamp": time.time()}} + msg = DeviceMessage(signals=data, metadata=metadata) + self.device_manager.connector.xadd( + MessageEndpoints.device_async_readback( + scan_id=self.scan_info.msg.scan_id, device=self.name + ), + msg_dict={"data": msg}, + max_size=1000, + expire=900, + ) diff --git a/superxas_bec/devices/test.py b/superxas_bec/devices/test.py index 67ff855..a8ba06a 100644 --- a/superxas_bec/devices/test.py +++ b/superxas_bec/devices/test.py @@ -10,7 +10,7 @@ from superxas_bec.devices.trigger import ContinuousSamplingMode, SamplingDone, T def mock_motor_move(pos: float) -> None: """Mock function to simulate motor movement.""" print(f"Mock Motor starts moving...") - time.sleep(0.5) + # time.sleep(0.5) print(f"Mock Motor has reached the target position {pos}") @@ -49,7 +49,7 @@ if __name__ == "__main__": print("Falcon is prepared for the scan.") trigger.start_csmpl.set(ContinuousSamplingMode.OFF).wait(timeout=5) cycles = max(int(exp_time * 5), 1) # Must be at least 1 cycle, each cycle is 0.2s - trigger.total_cycles.set(cycles).set(timeout=5) + trigger.total_cycles.set(cycles).wait(timeout=5) print(f"Trigger is prepared for the scan with {cycles} cycles.") #### End STAGE #### @@ -59,7 +59,12 @@ if __name__ == "__main__": for pos in positions: mock_motor_move(pos) # Trigger the devices + # time.sleep(0.5) + status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING) + # print(f"Erase start will be called {FalconAcquiringStatus(falcon.acquiring.get())}") falcon.erase_start.put(1) + print(f"Acquiring state after erase_start: {FalconAcquiringStatus(falcon.acquiring.get())}") + status.wait(timeout=5) time.sleep(0.4) status_smpl = TransitionStatus( trigger.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE] @@ -70,5 +75,11 @@ if __name__ == "__main__": status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) falcon.stop_all.put(1) status.wait(timeout=5) - time.sleep(0.5) # Simulate some processing time + time.sleep(0.4) # Simulate some processing time print(falcon.mca1.rois.roi0.count.get()) + print(falcon.mca1.elapsed_live_time.get()) + print(falcon.mca1.elapsed_real_time.get()) + print(falcon.max_elapsed_live.get()) + print(falcon.max_elapsed_real.get()) + time.sleep(2) + # time.sleep(0.2) diff --git a/superxas_bec/devices/trigger.py b/superxas_bec/devices/trigger.py index 810246d..763aa33 100644 --- a/superxas_bec/devices/trigger.py +++ b/superxas_bec/devices/trigger.py @@ -10,7 +10,7 @@ from ophyd import Device, DeviceStatus, EpicsSignal, EpicsSignalRO, Kind, Status from ophyd_devices import CompareStatus, TransitionStatus from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase -from superxas_bec.devices.falcon_direct import FalconAcquiringStatus, FalconSuperXASDirect +from superxas_bec.devices.falcon import FalconAcquiringStatus, FalconSuperXAS logger = bec_logger.logger @@ -71,7 +71,6 @@ class Trigger(PSIDeviceBase, TriggerControl): super().__init__(name=name, prefix=prefix, scan_info=scan_info, **kwargs) self.device_manager = device_manager self._pv_timeout = 5 - self._falcon_ready_timeout = 5 # seconds ######################################## # Beamline Specific Implementations # @@ -122,44 +121,35 @@ class Trigger(PSIDeviceBase, TriggerControl): falcon = self.device_manager.devices.get("falcon", None) if falcon is not None: - falcon: FalconSuperXASDirect + falcon: FalconSuperXAS + status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING) + self.cancel_on_stop(status) falcon.erase_start.put(1) - # falcon.erase_all.put(1) - # falcon.start_all.put(1) - time.sleep(0.4) # Wait for erase to complete - logger.info( - f"Erase and start acquiring for {falcon.name} at {time.time() - time_started}" + print(f"Acquiring state after erase_start: {FalconAcquiringStatus(falcon.acquiring.get())}") + status.wait(timeout=5) + time.sleep(0.4) + status_smpl = TransitionStatus( + self.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE] ) - try: - # pylint: disable=protected-access - CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING).wait( - timeout=self._pv_timeout - ) - except Exception as exc: - logger.error( - f"Falcon did not start acquiring. Current state {FalconAcquiringStatus(falcon.acquiring.get())}. " - ) - raise TimeoutError( - f"Falcon did not start acquiring within {self._pv_timeout} seconds." - ) from exc - - status_smpl = TransitionStatus(self.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE]) logger.info(f"Triggering sampling for {self.name} at {time.time() - time_started}") self.smpl.put(1) - status_smpl.wait(timeout=self._pv_timeout) logger.info(f"Sampling done for {self.name} at {time.time() - time_started}") - time.sleep(0.4) + self.cancel_on_stop(status_smpl) + status_smpl.wait() if falcon is not None: - # falcon.stop_all.put(1) - logger.info( - f"Waiting for Falcon to be ready after sampling at {time.time() - time_started}" - ) - # Wait for the Falcon to be done with acquiring data - # CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE).wait( - # timeout=self._falcon_ready_timeout - # ) - falcon.send_data() - # falcon.stop_all.put(1) + time.sleep(0.4)# Simulate some processing time + status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) + self.cancel_on_stop(status) + falcon.stop_all.put(1) + status.wait(timeout=5) + time.sleep(0.4) # Simulate some processing time + print(falcon.mca1.rois.roi0.count.get()) + print(falcon.mca1.elapsed_live_time.get()) + print(falcon.mca1.elapsed_real_time.get()) + print(falcon.max_elapsed_live.get()) + print(falcon.max_elapsed_real.get()) + # falcon.send_data() + time.sleep(2) # Sleep currently needed until Falcon acquiring/readout discussion is resolved return status_smpl def on_complete(self) -> DeviceStatus | StatusBase | None: -- 2.49.1 From f21ef6896c5e12a6296dd57e9e4382d51e3d4061 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Fri, 20 Jun 2025 07:21:16 +0200 Subject: [PATCH 19/42] wip update trigger card --- superxas_bec/devices/trigger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/superxas_bec/devices/trigger.py b/superxas_bec/devices/trigger.py index 763aa33..d677086 100644 --- a/superxas_bec/devices/trigger.py +++ b/superxas_bec/devices/trigger.py @@ -120,7 +120,7 @@ class Trigger(PSIDeviceBase, TriggerControl): logger.info(f"Triggering device {self.name} at {time_started}") falcon = self.device_manager.devices.get("falcon", None) - if falcon is not None: + if falcon is not None and falcon.enabled is True: falcon: FalconSuperXAS status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING) self.cancel_on_stop(status) @@ -136,7 +136,7 @@ class Trigger(PSIDeviceBase, TriggerControl): logger.info(f"Sampling done for {self.name} at {time.time() - time_started}") self.cancel_on_stop(status_smpl) status_smpl.wait() - if falcon is not None: + if falcon is not None and falcon.enabled is True: time.sleep(0.4)# Simulate some processing time status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) self.cancel_on_stop(status) -- 2.49.1 From 0da2ca391de3b513739ed5e3dea93ce42242a888 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Fri, 20 Jun 2025 07:24:00 +0200 Subject: [PATCH 20/42] wip test scrit --- superxas_bec/devices/test.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/superxas_bec/devices/test.py b/superxas_bec/devices/test.py index a8ba06a..0df4306 100644 --- a/superxas_bec/devices/test.py +++ b/superxas_bec/devices/test.py @@ -58,10 +58,7 @@ if __name__ == "__main__": print("Starting the scan...") for pos in positions: mock_motor_move(pos) - # Trigger the devices - # time.sleep(0.5) status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING) - # print(f"Erase start will be called {FalconAcquiringStatus(falcon.acquiring.get())}") falcon.erase_start.put(1) print(f"Acquiring state after erase_start: {FalconAcquiringStatus(falcon.acquiring.get())}") status.wait(timeout=5) @@ -81,5 +78,5 @@ if __name__ == "__main__": print(falcon.mca1.elapsed_real_time.get()) print(falcon.max_elapsed_live.get()) print(falcon.max_elapsed_real.get()) - time.sleep(2) - # time.sleep(0.2) + time.sleep(2) # #FIXME <- When removed, crashes always in second loop! Otherwise, it soemtimes works.. + -- 2.49.1 From 1c6d568ff2c44726266004e982043a865b0e3463 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Fri, 20 Jun 2025 07:38:17 +0200 Subject: [PATCH 21/42] fix exafs scan, disable falcon for the moment --- superxas_bec/device_configs/x10da_config_test.yaml | 2 +- superxas_bec/devices/trigger.py | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/superxas_bec/device_configs/x10da_config_test.yaml b/superxas_bec/device_configs/x10da_config_test.yaml index a9171de..1d8d28f 100644 --- a/superxas_bec/device_configs/x10da_config_test.yaml +++ b/superxas_bec/device_configs/x10da_config_test.yaml @@ -110,7 +110,7 @@ falcon: deviceConfig: prefix: 'X10DA-SITORO:' onFailure: raise - enabled: True + enabled: False softwareTrigger: False diff --git a/superxas_bec/devices/trigger.py b/superxas_bec/devices/trigger.py index d677086..aafa216 100644 --- a/superxas_bec/devices/trigger.py +++ b/superxas_bec/devices/trigger.py @@ -71,6 +71,7 @@ class Trigger(PSIDeviceBase, TriggerControl): super().__init__(name=name, prefix=prefix, scan_info=scan_info, **kwargs) self.device_manager = device_manager self._pv_timeout = 5 + self._trigger_index = 0 ######################################## # Beamline Specific Implementations # @@ -98,6 +99,7 @@ class Trigger(PSIDeviceBase, TriggerControl): """ self.start_csmpl.set(ContinuousSamplingMode.OFF).wait(timeout=self._pv_timeout) exp_time = self.scan_info.msg.scan_parameters["exp_time"] + self._trigger_index = 0 if self.scan_info.msg.scan_name != "exafs_scan": # TODO self.set_exposure_time(exp_time).wait(self._pv_timeout) @@ -116,6 +118,10 @@ class Trigger(PSIDeviceBase, TriggerControl): and then wait for the Falcon to be done with acquiring. This is to ensure that data of PVs is updated and the sampling is done before data is being read from the device. """ + if self.scan_info.msg.scan_name == "exafs_scan": + exp_time = self.scan_info.msg.scan_parameters['integ_time'][self._trigger_index] + self._trigger_index +=1 + self.set_exposure_time(exp_time).wait() time_started = time.time() logger.info(f"Triggering device {self.name} at {time_started}") @@ -127,15 +133,15 @@ class Trigger(PSIDeviceBase, TriggerControl): falcon.erase_start.put(1) print(f"Acquiring state after erase_start: {FalconAcquiringStatus(falcon.acquiring.get())}") status.wait(timeout=5) - time.sleep(0.4) + time.sleep(0.4) status_smpl = TransitionStatus( self.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE] ) logger.info(f"Triggering sampling for {self.name} at {time.time() - time_started}") self.smpl.put(1) - logger.info(f"Sampling done for {self.name} at {time.time() - time_started}") self.cancel_on_stop(status_smpl) status_smpl.wait() + logger.info(f"Sampling done for {self.name} at {time.time() - time_started}") if falcon is not None and falcon.enabled is True: time.sleep(0.4)# Simulate some processing time status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) @@ -164,4 +170,5 @@ class Trigger(PSIDeviceBase, TriggerControl): def set_exposure_time(self, value: float) -> DeviceStatus: """Utility method to set exposure time complying to device logic with cycle of min 0.2s.""" cycles = max(int(value * 5), 1) + logger.info(f"Device {self.name} set exp_time to {value} with N cycles: {cycles}") return self.total_cycles.set(cycles) -- 2.49.1 From abcf910faf0317f08c3e9069d6f7f28f3df260d2 Mon Sep 17 00:00:00 2001 From: appel_c Date: Mon, 23 Jun 2025 15:02:34 +0200 Subject: [PATCH 22/42] wip test cachannel vs pyepics --- superxas_bec/devices/test_script_cachannel.py | 61 +++++++++++++++++++ superxas_bec/devices/test_script_pyepics.py | 51 ++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 superxas_bec/devices/test_script_cachannel.py create mode 100644 superxas_bec/devices/test_script_pyepics.py diff --git a/superxas_bec/devices/test_script_cachannel.py b/superxas_bec/devices/test_script_cachannel.py new file mode 100644 index 0000000..77e000e --- /dev/null +++ b/superxas_bec/devices/test_script_cachannel.py @@ -0,0 +1,61 @@ +#!/usr/bin/python +# python script + +import time + +from CaChannel import CaChannel, ca + +I0 = CaChannel() +I0.searchw("X10DA-ES1-SAI_01:MEAN") + +Trigger = CaChannel() +Trigger.searchw("X10DA-ES1:SMPL") +TriggerDone = CaChannel() +TriggerDone.searchw("X10DA-ES1:SMPL-DONE") + +XMAPStart = CaChannel() +XMAPStart.searchw("X10DA-SITORO:EraseStart") +XMAPStop = CaChannel() +XMAPStop.searchw("X10DA-SITORO:StopAll") +XMAPAcquiring = CaChannel() +XMAPAcquiring.searchw("X10DA-SITORO:Acquiring") +XMAPICR = CaChannel() +XMAPICR.searchw("X10DA-SITORO:dxp1:InputCountRate") +XMAPOCR = CaChannel() +XMAPOCR.searchw("X10DA-SITORO:dxp1:OutputCountRate") +XMAPROI = CaChannel() +XMAPROI.searchw("X10DA-SITORO:mca1.R0") + + +for i in range(25): + start_time = time.time() + XMAPStart.putw(1) + + f = 0 + while f == 0: + time.sleep(0.05) + f = XMAPAcquiring.getw() + + Trigger.putw(1) + time.sleep(0.2) + + t = 0 + while t == 0: + time.sleep(0.05) + t = TriggerDone.getw() + + XMAPStop.putw(1) + + f = 1 + while f == 1: + time.sleep(0.05) + f = XMAPAcquiring.getw() + + time.sleep(0.1) + + i0 = I0.getw() + icr = XMAPICR.getw() + ocr = XMAPOCR.getw() + roi = XMAPROI.getw() + end_time = time.time() + print(f"time={end_time-start_time:.4f}", i0, icr, ocr, roi) diff --git a/superxas_bec/devices/test_script_pyepics.py b/superxas_bec/devices/test_script_pyepics.py new file mode 100644 index 0000000..095038a --- /dev/null +++ b/superxas_bec/devices/test_script_pyepics.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +# PyEpics version of the script + +import time + +import epics + +# Define PVs +I0 = epics.PV("X10DA-ES1-SAI_01:MEAN") +Trigger = epics.PV("X10DA-ES1:SMPL") +TriggerDone = epics.PV("X10DA-ES1:SMPL-DONE") + +XMAPStart = epics.PV("X10DA-SITORO:EraseStart") +XMAPStop = epics.PV("X10DA-SITORO:StopAll") +XMAPAcquiring = epics.PV("X10DA-SITORO:Acquiring") +XMAPICR = epics.PV("X10DA-SITORO:dxp1:InputCountRate") +XMAPOCR = epics.PV("X10DA-SITORO:dxp1:OutputCountRate") +XMAPROI = epics.PV("X10DA-SITORO:mca1.R0") + +# Wait for connections (optional) +for pv in [I0, Trigger, TriggerDone, XMAPStart, XMAPStop, XMAPAcquiring, XMAPICR, XMAPOCR, XMAPROI]: + pv.wait_for_connection(timeout=2) + +# Measurement loop +for i in range(25): + start_time = time.time() + XMAPStart.put(1, wait=True) + + while XMAPAcquiring.get() == 0: + time.sleep(0.05) + + Trigger.put(1, wait=True) + time.sleep(0.2) + + while TriggerDone.get() == 0: + time.sleep(0.05) + + XMAPStop.put(1, wait=True) + + while XMAPAcquiring.get() == 1: + time.sleep(0.05) + + time.sleep(0.1) + + i0 = I0.get() + icr = XMAPICR.get() + ocr = XMAPOCR.get() + roi = XMAPROI.get() + end_time = time.time() + + print(f"time={end_time - start_time:.4f}", i0, icr, ocr, roi) -- 2.49.1 From f643b50f5c94b06247cebc4c07c9e85d6b086ad0 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Mon, 23 Jun 2025 15:27:12 +0200 Subject: [PATCH 23/42] use pyepics automonitor and callbacks --- superxas_bec/devices/test_script_pyepics.py | 89 +++++++++++++-------- 1 file changed, 56 insertions(+), 33 deletions(-) diff --git a/superxas_bec/devices/test_script_pyepics.py b/superxas_bec/devices/test_script_pyepics.py index 095038a..3dc9926 100644 --- a/superxas_bec/devices/test_script_pyepics.py +++ b/superxas_bec/devices/test_script_pyepics.py @@ -2,50 +2,73 @@ # PyEpics version of the script import time +import threading import epics -# Define PVs -I0 = epics.PV("X10DA-ES1-SAI_01:MEAN") -Trigger = epics.PV("X10DA-ES1:SMPL") -TriggerDone = epics.PV("X10DA-ES1:SMPL-DONE") +def main(): + # Define PVs + I0 = epics.PV("X10DA-ES1-SAI_01:MEAN") + Trigger = epics.PV("X10DA-ES1:SMPL") + TriggerDone = epics.PV("X10DA-ES1:SMPL-DONE", auto_monitor=True) -XMAPStart = epics.PV("X10DA-SITORO:EraseStart") -XMAPStop = epics.PV("X10DA-SITORO:StopAll") -XMAPAcquiring = epics.PV("X10DA-SITORO:Acquiring") -XMAPICR = epics.PV("X10DA-SITORO:dxp1:InputCountRate") -XMAPOCR = epics.PV("X10DA-SITORO:dxp1:OutputCountRate") -XMAPROI = epics.PV("X10DA-SITORO:mca1.R0") + XMAPStart = epics.PV("X10DA-SITORO:EraseStart") + XMAPStop = epics.PV("X10DA-SITORO:StopAll") + XMAPAcquiring = epics.PV("X10DA-SITORO:Acquiring", auto_monitor=True) + XMAPICR = epics.PV("X10DA-SITORO:dxp1:InputCountRate") + XMAPOCR = epics.PV("X10DA-SITORO:dxp1:OutputCountRate") + XMAPROI = epics.PV("X10DA-SITORO:mca1.R0") -# Wait for connections (optional) -for pv in [I0, Trigger, TriggerDone, XMAPStart, XMAPStop, XMAPAcquiring, XMAPICR, XMAPOCR, XMAPROI]: - pv.wait_for_connection(timeout=2) + + acquire_started = threading.Event() + acquire_stopped = threading.Event() + trigger_done = threading.Event() -# Measurement loop -for i in range(25): - start_time = time.time() - XMAPStart.put(1, wait=True) + def acquiring_cb(pvname=None, value=None, **kwargs): + if value == 1: + acquire_started.set() + elif value == 0: + acquire_stopped.set() - while XMAPAcquiring.get() == 0: - time.sleep(0.05) + def trigger_done_cb(pvname=None, value=None, **kwargs): + if value == 1: + trigger_done.set() - Trigger.put(1, wait=True) - time.sleep(0.2) + # Wait for connections (optional) + for pv in [I0, Trigger, TriggerDone, XMAPStart, XMAPStop, XMAPAcquiring, XMAPICR, XMAPOCR, XMAPROI]: + pv.wait_for_connection(timeout=2) - while TriggerDone.get() == 0: - time.sleep(0.05) + TriggerDone.add_callback(trigger_done_cb) + XMAPAcquiring.add_callback(acquiring_cb) - XMAPStop.put(1, wait=True) + # Measurement loop + for i in range(25): + acquire_started.clear() + acquire_stopped.clear() + trigger_done.clear() - while XMAPAcquiring.get() == 1: - time.sleep(0.05) + start_time = time.time() + XMAPStart.put(1, wait=False) - time.sleep(0.1) + acquire_started.wait(timeout=5) - i0 = I0.get() - icr = XMAPICR.get() - ocr = XMAPOCR.get() - roi = XMAPROI.get() - end_time = time.time() + Trigger.put(1, wait=False) - print(f"time={end_time - start_time:.4f}", i0, icr, ocr, roi) + trigger_done.wait(3) + + XMAPStop.put(1, wait=True) + + acquire_stopped.wait(5) + + time.sleep(0.1) + + i0 = I0.get() + icr = XMAPICR.get() + ocr = XMAPOCR.get() + roi = XMAPROI.get() + end_time = time.time() + + print(f"time={end_time - start_time:.4f}", i0, icr, ocr, roi) + +if __name__=="__main__": + main() -- 2.49.1 From 6f6d19ac0feddb79fc2cebdf7ecc6d0785c1089d Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Tue, 24 Jun 2025 13:52:46 +0200 Subject: [PATCH 24/42] updates beamline test scripts --- superxas_bec/devices/falcon_direct.py | 20 ++++++------ superxas_bec/devices/test.py | 35 +++++++++++++++------ superxas_bec/devices/test_script_pyepics.py | 2 +- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py index eee108e..9707e8b 100644 --- a/superxas_bec/devices/falcon_direct.py +++ b/superxas_bec/devices/falcon_direct.py @@ -36,18 +36,18 @@ class FalconAcquiringStatus(int, enum.Enum): class DXPControl(Device): """DXP Control Device for Falcon detector""" - input_count_rate = Cpt(EpicsSignalRO, "InputCountRate", kind=Kind.omitted, auto_monitor=True) - output_count_rate = Cpt(EpicsSignalRO, "OutputCountRate", kind=Kind.omitted, auto_monitor=True) + input_count_rate = Cpt(EpicsSignalRO, "InputCountRate", kind=Kind.omitted) + output_count_rate = Cpt(EpicsSignalRO, "OutputCountRate", kind=Kind.omitted) elapsed_real_time = Cpt(EpicsSignalRO, "ElapsedRealTime", kind=Kind.omitted) class MCAControl(Device): """MCA Control Device for Falcon detector""" - spectrum = Cpt(EpicsSignalRO, ".VAL", kind=Kind.omitted, auto_monitor=True) - roi_count = Cpt(EpicsSignalRO, ".R0", kind=Kind.omitted, auto_monitor=True) - roi_label = Cpt(EpicsSignal, ".R0NM", kind=Kind.omitted) - elapsed_real_time = Cpt(EpicsSignalRO, ".ERTM", kind=Kind.omitted, auto_monitor=True) + spectrum = Cpt(EpicsSignalRO, ".VAL", kind=Kind.omitted) + roi0_count = Cpt(EpicsSignalRO, ".R0", kind=Kind.omitted) + roi0_label = Cpt(EpicsSignal, ".R0NM", kind=Kind.omitted) + elapsed_real_time = Cpt(EpicsSignalRO, ".ERTM", kind=Kind.omitted) class DeadTimeCorrectedCounts(Signal): @@ -213,7 +213,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): # self.stop_all.put(1, use_complete=True) if self.acquiring.get() == FalconAcquiringStatus.ACQUIRING: status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) - self.stop_all.put(1) + self.stop_all.set(1).wait(self._pv_timeout) try: status.wait(self._pv_timeout) except Exception as exc: @@ -225,7 +225,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): def on_unstage(self) -> DeviceStatus | StatusBase | None: """Called while unstaging the device.""" - self.stop_all.put(1) + self.stop_all.set(1).wait(self._pv_timeout) def on_pre_scan(self) -> DeviceStatus | StatusBase | None: """Called right before the scan starts on all devices automatically.""" @@ -241,7 +241,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): def on_stop(self) -> None: """Called when the device is stopped.""" - self.stop_all.put(1) + self.stop_all.set(1).wait(self._pv_timeout) def send_data(self): """ @@ -257,7 +257,7 @@ class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): self.ocr.put(self.dxp1.output_count_rate.get()) self.elap_real_time.put(self.mca1.elapsed_real_time.get()) self.roi0_count.put(self.mca1.roi_count.get()) - self.dead_cor_roi0_count.put(self.dead_time_cor_cnts1.get()) + # self.dead_cor_roi0_count.put(self.dead_time_cor_cnts1.get()) self._send_preview_async() logger.info(f"Data sent for {self.name} at {time.time()- time_started}") diff --git a/superxas_bec/devices/test.py b/superxas_bec/devices/test.py index 0df4306..1f8f872 100644 --- a/superxas_bec/devices/test.py +++ b/superxas_bec/devices/test.py @@ -1,9 +1,10 @@ import time +import epics import numpy as np from ophyd_devices import CompareStatus, TransitionStatus -from superxas_bec.devices.falcon import FalconAcquiringStatus, FalconSuperXAS +from superxas_bec.devices.falcon_direct import FalconAcquiringStatus, FalconControlDirect as FalconSuperXAS from superxas_bec.devices.trigger import ContinuousSamplingMode, SamplingDone, Trigger @@ -13,9 +14,15 @@ def mock_motor_move(pos: float) -> None: # time.sleep(0.5) print(f"Mock Motor has reached the target position {pos}") +def sleep_poll(total_sleep:float): + sleep_timer = 0.01 + # time.sleep(total_sleep) + for ii in range(int(total_sleep/sleep_timer)): + time.sleep(sleep_timer) + epics.poll() if __name__ == "__main__": - + # time.sleep(20) # Give time to connect pyspy # Exposure time 0.6s exp_time = 0.6 # steps = 10 @@ -62,21 +69,29 @@ if __name__ == "__main__": falcon.erase_start.put(1) print(f"Acquiring state after erase_start: {FalconAcquiringStatus(falcon.acquiring.get())}") status.wait(timeout=5) - time.sleep(0.4) + sleep_poll(0.4) + # time.sleep(0.4) status_smpl = TransitionStatus( trigger.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE] ) trigger.smpl.put(1) status_smpl.wait() - time.sleep(0.4) + sleep_poll(0.4) + # time.sleep(0.4) status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) falcon.stop_all.put(1) status.wait(timeout=5) - time.sleep(0.4) # Simulate some processing time - print(falcon.mca1.rois.roi0.count.get()) - print(falcon.mca1.elapsed_live_time.get()) + sleep_poll(0.4) + # time.sleep(0.4) # Simulate some processing time + # print(falcon.mca1.rois.roi0.count.get()) + print(falcon.mca1.roi0_count.get()) print(falcon.mca1.elapsed_real_time.get()) - print(falcon.max_elapsed_live.get()) - print(falcon.max_elapsed_real.get()) - time.sleep(2) # #FIXME <- When removed, crashes always in second loop! Otherwise, it soemtimes works.. + print(falcon.dxp1.input_count_rate.get()) + print(falcon.dxp1.output_count_rate.get()) + # print(falcon.mca1.elapsed_real_time.get()) + # print(falcon.max_elapsed_live.get()) + # print(falcon.max_elapsed_real.get()) + sleep_poll(2) + # time.sleep(2) # #FIXME <- When removed, crashes always in second loop! Otherwise, it soemtimes works.. + diff --git a/superxas_bec/devices/test_script_pyepics.py b/superxas_bec/devices/test_script_pyepics.py index 3dc9926..c348f6d 100644 --- a/superxas_bec/devices/test_script_pyepics.py +++ b/superxas_bec/devices/test_script_pyepics.py @@ -42,7 +42,7 @@ def main(): XMAPAcquiring.add_callback(acquiring_cb) # Measurement loop - for i in range(25): + for i in range(500): acquire_started.clear() acquire_stopped.clear() trigger_done.clear() -- 2.49.1 From a70478db2d3bbef1bfe8a1f9710b9c4405288413 Mon Sep 17 00:00:00 2001 From: appel_c Date: Tue, 24 Jun 2025 14:10:58 +0200 Subject: [PATCH 25/42] ophyd test script --- superxas_bec/devices/test_script_ophyd.py | 66 +++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 superxas_bec/devices/test_script_ophyd.py diff --git a/superxas_bec/devices/test_script_ophyd.py b/superxas_bec/devices/test_script_ophyd.py new file mode 100644 index 0000000..863c403 --- /dev/null +++ b/superxas_bec/devices/test_script_ophyd.py @@ -0,0 +1,66 @@ +import time + +from ophyd import Component as Cpt +from ophyd import Device, EpicsSignal, EpicsSignalRO +from ophyd.status import SubscriptionStatus + + +class DummyDevice(Device): + """ + A dummy device for testing purposes. + """ + + i_0 = Cpt(EpicsSignalRO, "ES1-SAI_01:MEAN", doc="I0 signal") + trigger = Cpt(EpicsSignal, "ES1:SMPL", doc="Trigger signal") + trigger_done = Cpt(EpicsSignalRO, "ES1:SMPL-DONE", auto_monitor=True, doc="Trigger done signal") + + xmap_start = Cpt(EpicsSignal, "SITORO:EraseStart", doc="XMAP start signal") + xmap_stop = Cpt(EpicsSignal, "SITORO:StopAll", doc="XMAP stop signal") + xmap_acquiring = Cpt( + EpicsSignalRO, "SITORO:Acquiring", auto_monitor=True, doc="XMAP acquiring signal" + ) + xmap_icr = Cpt(EpicsSignalRO, "SITORO:dxp1:InputCountRate", doc="XMAP input count rate") + xmap_ocr = Cpt(EpicsSignalRO, "SITORO:dxp1:OutputCountRate", doc="XMAP output count rate") + xmap_roi = Cpt(EpicsSignalRO, "SITORO:mca1.R0", doc="XMAP ROI signal") + + +def state_changed_callback(*, value, old_value, **kwargs): + """Callback for acquiring signal changes.""" + if old_value == 0 and value == 1 or old_value == 1 and value == 0: + print(f"State changed: {old_value} -> {value}") + return True + return False + + +if __name__ == "__main__": + # Create an instance of the dummy device + print("Initializing DummyDevice...") + time_started = time.time() + + device = DummyDevice(name="test_device", prefix="X10DA-") + + device.wait_for_connection(timeout=50, all_signals=True) + print(f"Device initialized in {time.time() - time_started:.2f} seconds.") + + for i in range(500): + start_time = time.time() + status = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) + device.xmap_start.put(1) + status.wait(timeout=5) + + status2 = SubscriptionStatus(device.trigger_done, state_changed_callback) + device.trigger.put(1) + status2.wait(timeout=5) + + status3 = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) + device.xmap_stop.put(1) + status3.wait(timeout=5) + + time.sleep(0.1) + + i0 = device.i_0.get() + icr = device.xmap_icr.get() + ocr = device.xmap_ocr.get() + roi = device.xmap_roi.get() + + print(f"time={time.time() - start_time:.4f}", i0, icr, ocr, roi) -- 2.49.1 From 83b4092326d8bee263ce6604d02ac86e45329bf7 Mon Sep 17 00:00:00 2001 From: appel_c Date: Tue, 24 Jun 2025 14:48:05 +0200 Subject: [PATCH 26/42] wip --- superxas_bec/devices/test_script_ophyd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/superxas_bec/devices/test_script_ophyd.py b/superxas_bec/devices/test_script_ophyd.py index 863c403..0b2f783 100644 --- a/superxas_bec/devices/test_script_ophyd.py +++ b/superxas_bec/devices/test_script_ophyd.py @@ -11,7 +11,7 @@ class DummyDevice(Device): """ i_0 = Cpt(EpicsSignalRO, "ES1-SAI_01:MEAN", doc="I0 signal") - trigger = Cpt(EpicsSignal, "ES1:SMPL", doc="Trigger signal") + trigger_smpl = Cpt(EpicsSignal, "ES1:SMPL", doc="Trigger signal") trigger_done = Cpt(EpicsSignalRO, "ES1:SMPL-DONE", auto_monitor=True, doc="Trigger done signal") xmap_start = Cpt(EpicsSignal, "SITORO:EraseStart", doc="XMAP start signal") @@ -49,7 +49,7 @@ if __name__ == "__main__": status.wait(timeout=5) status2 = SubscriptionStatus(device.trigger_done, state_changed_callback) - device.trigger.put(1) + device.trigger_smpl.put(1) status2.wait(timeout=5) status3 = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) -- 2.49.1 From a511153db0ad6ca7d95bdf634072434009fcd31d Mon Sep 17 00:00:00 2001 From: appel_c Date: Tue, 24 Jun 2025 14:50:12 +0200 Subject: [PATCH 27/42] wip --- superxas_bec/devices/test_script_ophyd.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/superxas_bec/devices/test_script_ophyd.py b/superxas_bec/devices/test_script_ophyd.py index 0b2f783..ab36c92 100644 --- a/superxas_bec/devices/test_script_ophyd.py +++ b/superxas_bec/devices/test_script_ophyd.py @@ -32,6 +32,14 @@ def state_changed_callback(*, value, old_value, **kwargs): return False +def acquire_stoped(*, value, old_value, **kwargs): + """Callback for acquisition stop.""" + if value == 0: + print("Acquisition stopped.") + return True + return False + + if __name__ == "__main__": # Create an instance of the dummy device print("Initializing DummyDevice...") @@ -41,6 +49,9 @@ if __name__ == "__main__": device.wait_for_connection(timeout=50, all_signals=True) print(f"Device initialized in {time.time() - time_started:.2f} seconds.") + status = SubscriptionStatus(device.xmap_acquiring, acquire_stoped) + device.xmap_stop.put(1) + status.wait(timeout=5) for i in range(500): start_time = time.time() -- 2.49.1 From 1b3dab2ff3296be270a4620d697144b134df68ac Mon Sep 17 00:00:00 2001 From: appel_c Date: Tue, 24 Jun 2025 14:56:07 +0200 Subject: [PATCH 28/42] add compare and transition status --- superxas_bec/devices/test_script_ophyd.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/superxas_bec/devices/test_script_ophyd.py b/superxas_bec/devices/test_script_ophyd.py index ab36c92..2215093 100644 --- a/superxas_bec/devices/test_script_ophyd.py +++ b/superxas_bec/devices/test_script_ophyd.py @@ -3,6 +3,7 @@ import time from ophyd import Component as Cpt from ophyd import Device, EpicsSignal, EpicsSignalRO from ophyd.status import SubscriptionStatus +from ophyd_devices import CompareStatus, TransitionStatus class DummyDevice(Device): @@ -49,21 +50,25 @@ if __name__ == "__main__": device.wait_for_connection(timeout=50, all_signals=True) print(f"Device initialized in {time.time() - time_started:.2f} seconds.") - status = SubscriptionStatus(device.xmap_acquiring, acquire_stoped) + # status = SubscriptionStatus(device.xmap_acquiring, acquire_stoped) + status = CompareStatus(device.xmap_acquiring, 0) device.xmap_stop.put(1) status.wait(timeout=5) for i in range(500): start_time = time.time() - status = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) + status = CompareStatus(device.xmap_acquiring, 1) + # status = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) device.xmap_start.put(1) status.wait(timeout=5) - status2 = SubscriptionStatus(device.trigger_done, state_changed_callback) + # status2 = SubscriptionStatus(device.trigger_done, state_changed_callback) + status2 = CompareStatus(device.trigger_done, 0) device.trigger_smpl.put(1) status2.wait(timeout=5) - status3 = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) + # status3 = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) + status3 = CompareStatus(device.xmap_acquiring, 0) device.xmap_stop.put(1) status3.wait(timeout=5) -- 2.49.1 From a600c09b977df3aad6f30c0f73fee43f4097e04a Mon Sep 17 00:00:00 2001 From: appel_c Date: Tue, 24 Jun 2025 14:58:05 +0200 Subject: [PATCH 29/42] wi --- superxas_bec/devices/test_script_ophyd.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/superxas_bec/devices/test_script_ophyd.py b/superxas_bec/devices/test_script_ophyd.py index 2215093..d9c23b2 100644 --- a/superxas_bec/devices/test_script_ophyd.py +++ b/superxas_bec/devices/test_script_ophyd.py @@ -50,25 +50,25 @@ if __name__ == "__main__": device.wait_for_connection(timeout=50, all_signals=True) print(f"Device initialized in {time.time() - time_started:.2f} seconds.") - # status = SubscriptionStatus(device.xmap_acquiring, acquire_stoped) - status = CompareStatus(device.xmap_acquiring, 0) + status = SubscriptionStatus(device.xmap_acquiring, acquire_stoped) + # status = CompareStatus(device.xmap_acquiring, 0) device.xmap_stop.put(1) status.wait(timeout=5) for i in range(500): start_time = time.time() - status = CompareStatus(device.xmap_acquiring, 1) - # status = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) + status = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) + # status = CompareStatus(device.xmap_acquiring, 1) device.xmap_start.put(1) status.wait(timeout=5) - # status2 = SubscriptionStatus(device.trigger_done, state_changed_callback) - status2 = CompareStatus(device.trigger_done, 0) + status2 = SubscriptionStatus(device.trigger_done, state_changed_callback) + # status2 = CompareStatus(device.trigger_done, 0) device.trigger_smpl.put(1) status2.wait(timeout=5) - # status3 = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) - status3 = CompareStatus(device.xmap_acquiring, 0) + status3 = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) + # status3 = CompareStatus(device.xmap_acquiring, 0) device.xmap_stop.put(1) status3.wait(timeout=5) -- 2.49.1 From 2a8c8b6f1c5d213ccf0652c657fe228d34827972 Mon Sep 17 00:00:00 2001 From: appel_c Date: Tue, 24 Jun 2025 15:00:11 +0200 Subject: [PATCH 30/42] w --- superxas_bec/devices/test_script_ophyd.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/superxas_bec/devices/test_script_ophyd.py b/superxas_bec/devices/test_script_ophyd.py index d9c23b2..bfb7646 100644 --- a/superxas_bec/devices/test_script_ophyd.py +++ b/superxas_bec/devices/test_script_ophyd.py @@ -50,25 +50,25 @@ if __name__ == "__main__": device.wait_for_connection(timeout=50, all_signals=True) print(f"Device initialized in {time.time() - time_started:.2f} seconds.") - status = SubscriptionStatus(device.xmap_acquiring, acquire_stoped) - # status = CompareStatus(device.xmap_acquiring, 0) + # status = SubscriptionStatus(device.xmap_acquiring, acquire_stoped) + status = CompareStatus(device.xmap_acquiring, 0) device.xmap_stop.put(1) status.wait(timeout=5) for i in range(500): start_time = time.time() - status = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) - # status = CompareStatus(device.xmap_acquiring, 1) + # status = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) + status = CompareStatus(device.xmap_acquiring, 1) device.xmap_start.put(1) status.wait(timeout=5) - status2 = SubscriptionStatus(device.trigger_done, state_changed_callback) - # status2 = CompareStatus(device.trigger_done, 0) + # status2 = SubscriptionStatus(device.trigger_done, state_changed_callback) + status2 = CompareStatus(device.trigger_done, 0) device.trigger_smpl.put(1) status2.wait(timeout=5) - status3 = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) - # status3 = CompareStatus(device.xmap_acquiring, 0) + # status3 = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) + status3 = CompareStatus(device.xmap_acquiring, 0) device.xmap_stop.put(1) status3.wait(timeout=5) -- 2.49.1 From a7d3bd3ea03fe761b2034901940691fbfbba06b2 Mon Sep 17 00:00:00 2001 From: appel_c Date: Tue, 24 Jun 2025 17:02:33 +0200 Subject: [PATCH 31/42] wip falcon slim --- superxas_bec/devices/falcon.py | 32 ++++-- superxas_bec/devices/falcon_slim.py | 150 ++++++++++++++++++++++++++++ superxas_bec/devices/trigger.py | 31 ++---- 3 files changed, 182 insertions(+), 31 deletions(-) create mode 100644 superxas_bec/devices/falcon_slim.py diff --git a/superxas_bec/devices/falcon.py b/superxas_bec/devices/falcon.py index a7ac808..94b1f02 100644 --- a/superxas_bec/devices/falcon.py +++ b/superxas_bec/devices/falcon.py @@ -2,15 +2,16 @@ import enum import time + import numpy as np from bec_lib.devicemanager import ScanInfo +from bec_lib.endpoints import MessageEndpoints from bec_lib.logger import bec_logger from bec_lib.messages import DeviceMessage -from bec_lib.endpoints import MessageEndpoints -from ophyd_devices import CompareStatus, TransitionStatus from ophyd import Component as Cpt -from ophyd import DeviceStatus, Kind, Signal, StatusBase, Staged +from ophyd import DeviceStatus, Kind, Signal, Staged, StatusBase from ophyd.status import SubscriptionStatus +from ophyd_devices import CompareStatus, TransitionStatus from ophyd_devices.devices.dxp import EpicsDXPFalcon, EpicsMCARecord, Falcon from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase @@ -23,7 +24,8 @@ class FalconAcquiringStatus(int, enum.Enum): DONE = 0 ACQUIRING = 1 -def compute_dead_time_corrected_signal(icr:float, ocr:float, roi:float, ert:float): + +def compute_dead_time_corrected_signal(icr: float, ocr: float, roi: float, ert: float): _dead_time = 1.182e-7 if icr == 0 or ocr == 0: return 0 @@ -89,8 +91,7 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): preview = Cpt( Signal, name="preview", kind=Kind.omitted, doc="Preview signal for Falcon detector" ) - icr = Cpt( - Signal, name="icr", kind=Kind.normal) + icr = Cpt(Signal, name="icr", kind=Kind.normal) ocr = Cpt(Signal, name="icr", kind=Kind.normal) elap_real_time = Cpt(Signal, name="icr", kind=Kind.normal) roi0_count = Cpt(Signal, name="icr", kind=Kind.normal) @@ -98,14 +99,21 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): Signal, name="dead_cor_roi0_count", doc="Async dead time corrected ROI 0 count signal", - kind=Kind.normal + kind=Kind.normal, ) ######################################## # Beamline Specific Implementations # ######################################## - def __init__(self, name: str, prefix: str = "", scan_info: ScanInfo | None = None, device_manager=None, **kwargs): + def __init__( + self, + name: str, + prefix: str = "", + scan_info: ScanInfo | None = None, + device_manager=None, + **kwargs, + ): """ Initialize Falcon device. @@ -115,7 +123,9 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): scan_info (ScanInfo): Information about the scan **kwargs: Additional keyword arguments """ - super().__init__(name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs) + super().__init__( + name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs + ) self.device_manager = device_manager self.mca1.stage_sigs = {} self._pv_timeout = 5 @@ -195,14 +205,14 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): logger.info(f"Sending data for {self.name} at {time_started}") icr = self.dxp1.input_count_rate.get() ocr = self.dxp1.output_count_rate.get() - roi = self.mca1.elapsed_real_time.get() + roi = self.mca1.rois.count.get() ert = self.mca1.elapsed_real_time.get() self.icr.put(icr) logger.info(f"Data to plot {self.icr.get()}") self.ocr.put(ocr) self.elap_real_time.put(ert) self.roi0_count.put(roi) - self.dead_cor_roi0_count.put(compute_dead_time_corrected_signal(icr,ocr,roi,ert)) + self.dead_cor_roi0_count.put(compute_dead_time_corrected_signal(icr, ocr, roi, ert)) # self._send_preview_async() logger.info(f"Data sent for {self.name} at {time.time()- time_started}") diff --git a/superxas_bec/devices/falcon_slim.py b/superxas_bec/devices/falcon_slim.py new file mode 100644 index 0000000..ef597d3 --- /dev/null +++ b/superxas_bec/devices/falcon_slim.py @@ -0,0 +1,150 @@ +import enum + +import numpy as np +from bec_lib.logger import bec_logger +from ophyd import Component as Cpt +from ophyd import Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV, Kind, Signal +from ophyd_devices import CompareStatus, DeviceStatus, PreviewSignal, StatusBase +from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase + +logger = bec_logger.logger + + +class FalconAcquiringStatus(int, enum.Enum): + """Status of Falcon""" + + DONE = 0 + ACQUIRING = 1 + + +class FalconControlSlim(Device): + """Slim Falcon Control class for SuperXAS. prefix: 'X10DA-SITORO:'""" + + start = Cpt(EpicsSignal, "EraseStart", doc="XMAP start signal") + stop = Cpt(EpicsSignal, "StopAll", doc="XMAP stop signal") + acquiring = Cpt(EpicsSignalRO, "Acquiring", auto_monitor=True, doc="XMAP acquiring signal") + icr = Cpt(EpicsSignalRO, "dxp1:InputCountRate", doc="XMAP input count rate") + ocr = Cpt(EpicsSignalRO, "dxp1:OutputCountRate", doc="XMAP output count rate") + ert = Cpt(EpicsSignalRO, "mca1.ERTM", doc="XMAP elapsed real time") + roi = Cpt(EpicsSignalRO, "mca1.R0", kind=Kind.hinted, doc="XMAP ROI signal") + label = Cpt(EpicsSignalRO, "mca1.R0NM", kind=Kind.config, doc="XMAP ROI signal") + spectrum_val = Cpt(EpicsSignalRO, "mca1.VAL", kind=Kind.omitted) + + # Preview Signal for Falcon detector + spectrum = Cpt( + PreviewSignal, name="spectrum", ndim=1, doc="Preview signal for Falcon detector spectrum" + ) + + # Configuration attributes + collect_mode = Cpt(EpicsSignal, "CollectMode", doc="Collect mode signal") + preset_real_time = Cpt(EpicsSignalWithRBV, "PresetRealTime", doc="Preset real time signal") + + # Computed signal for dead time corrected counts + dead_cor_roi0_count = Cpt(Signal, "dead_cor_roi0_count", doc="Dead time corrected ROI 0 count") + + +def compute_dead_time_corrected_signal(icr: float, ocr: float, roi: float, ert: float): + _dead_time = 1.182e-7 + if icr == 0 or ocr == 0: + return 0 + + # Check that relative change is large enough + test = 1e9 + test_icr = icr + n = 0 + while test > _dead_time and n < 30: + try: + true_icr = icr * np.exp(test_icr * _dead_time) + test = (true_icr - test_icr) / test_icr + test_icr = true_icr + n += 1 + except Exception as e: # pylint: disable=broad-except + logger.info(f"Error in computation of deadtime corrected signal, error: {e}") + return 0 + + # Return corrected roi counts + cor_roi_cnts = 0 + if ocr * ert != 0: + cor_roi_cnts = roi * true_icr / (ocr * ert) + return cor_roi_cnts + + +class FalconSuperXASSlim(PSIDeviceBase, FalconControlSlim): + """Slim Falcon implementation at SuperXAS. prefix: 'X10DA-SITORO:'""" + + def __init__(self, *, name, prefix="", scan_info=None, device_manager=None, **kwargs): + super().__init__( + name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs + ) + self._pv_timeout = 5 # seconds + + 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. + """ + + def on_stage(self) -> DeviceStatus | StatusBase | None: + """ + Called while staging the device. + + Information about the upcoming scan can be accessed from the scan_info (self.scan_info.msg) object. + """ + if self.acquiring.get() != FalconAcquiringStatus.DONE: + logger.info(f"Falcon state was {self.acquiring.get()} during stage. Calling stop_all") + status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) + self.cancel_on_stop(status) + self.stop_all.put(1) + status.wait(timeout=self._pv_timeout) + + self.collect_mode.set(0).wait(timeout=self._pv_timeout) + self.preset_real_time.set(0).wait(timeout=self._pv_timeout) + + def on_unstage(self) -> DeviceStatus | StatusBase | None: + """Called while unstaging the device.""" + if self.acquiring.get() != FalconAcquiringStatus.DONE: + self.stop_all.put(1) + status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) + self.cancel_on_stop(status) + return status + + def on_pre_scan(self) -> DeviceStatus | StatusBase | None: + """Called right before the scan starts on all devices automatically.""" + + def on_trigger(self) -> DeviceStatus | StatusBase | None: + """Called when the device is triggered.""" + + def on_complete(self) -> DeviceStatus | StatusBase | None: + """Called to inquire if a device has completed a scans.""" + + def on_kickoff(self) -> DeviceStatus | StatusBase | 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.stop_all.put(1) + + def update_data(self): + """ + Set the dead time corrected signal based on input count rate, output count rate, ROI count, and elapsed real time. + + Parameters: + icr (float): Input count rate. + ocr (float): Output count rate. + roi (float): ROI count. + ert (float): Elapsed real time. + """ + + dead_time_corrected_signal = compute_dead_time_corrected_signal( + self.icr.get(), self.ocr.get(), self.roi.get(), self.ert.get() + ) + self.dead_cor_roi0_count.put(dead_time_corrected_signal) + self.spectrum.put(self.spectrum_val.get()) diff --git a/superxas_bec/devices/trigger.py b/superxas_bec/devices/trigger.py index aafa216..1240857 100644 --- a/superxas_bec/devices/trigger.py +++ b/superxas_bec/devices/trigger.py @@ -10,7 +10,7 @@ from ophyd import Device, DeviceStatus, EpicsSignal, EpicsSignalRO, Kind, Status from ophyd_devices import CompareStatus, TransitionStatus from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase -from superxas_bec.devices.falcon import FalconAcquiringStatus, FalconSuperXAS +from superxas_bec.devices.falcon_slim import FalconAcquiringStatus, FalconSuperXASSlim logger = bec_logger.logger @@ -119,43 +119,34 @@ class Trigger(PSIDeviceBase, TriggerControl): and the sampling is done before data is being read from the device. """ if self.scan_info.msg.scan_name == "exafs_scan": - exp_time = self.scan_info.msg.scan_parameters['integ_time'][self._trigger_index] - self._trigger_index +=1 + exp_time = self.scan_info.msg.scan_parameters["integ_time"][self._trigger_index] + self._trigger_index += 1 self.set_exposure_time(exp_time).wait() time_started = time.time() logger.info(f"Triggering device {self.name} at {time_started}") falcon = self.device_manager.devices.get("falcon", None) if falcon is not None and falcon.enabled is True: - falcon: FalconSuperXAS + falcon: FalconSuperXASSlim status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING) self.cancel_on_stop(status) falcon.erase_start.put(1) - print(f"Acquiring state after erase_start: {FalconAcquiringStatus(falcon.acquiring.get())}") - status.wait(timeout=5) - time.sleep(0.4) - status_smpl = TransitionStatus( - self.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE] - ) + status.wait(timeout=self._pv_timeout) + time.sleep(0.4) + status_smpl = TransitionStatus(self.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE]) logger.info(f"Triggering sampling for {self.name} at {time.time() - time_started}") self.smpl.put(1) self.cancel_on_stop(status_smpl) - status_smpl.wait() + status_smpl.wait(timeout=self._pv_timeout) logger.info(f"Sampling done for {self.name} at {time.time() - time_started}") if falcon is not None and falcon.enabled is True: - time.sleep(0.4)# Simulate some processing time + time.sleep(0.4) # Simulate some processing time status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) self.cancel_on_stop(status) falcon.stop_all.put(1) - status.wait(timeout=5) + status.wait(timeout=self._pv_timeout) time.sleep(0.4) # Simulate some processing time - print(falcon.mca1.rois.roi0.count.get()) - print(falcon.mca1.elapsed_live_time.get()) - print(falcon.mca1.elapsed_real_time.get()) - print(falcon.max_elapsed_live.get()) - print(falcon.max_elapsed_real.get()) - # falcon.send_data() - time.sleep(2) # Sleep currently needed until Falcon acquiring/readout discussion is resolved + falcon.update_data() return status_smpl def on_complete(self) -> DeviceStatus | StatusBase | None: -- 2.49.1 From f70445f7831c724eb061306298d567f4ab2e854f Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Tue, 24 Jun 2025 17:30:17 +0200 Subject: [PATCH 32/42] wip working falconslim --- .../x10da_config_falcon_test.yaml | 13 +++++++-- superxas_bec/devices/falcon_slim.py | 28 +++++++++---------- superxas_bec/devices/trigger.py | 2 +- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/superxas_bec/device_configs/x10da_config_falcon_test.yaml b/superxas_bec/device_configs/x10da_config_falcon_test.yaml index 1118cc9..efacbb5 100644 --- a/superxas_bec/device_configs/x10da_config_falcon_test.yaml +++ b/superxas_bec/device_configs/x10da_config_falcon_test.yaml @@ -1,11 +1,11 @@ falcon: description: Falcon Sitoro detector - deviceClass: superxas_bec.devices.falcon.FalconSuperXAS + deviceClass: superxas_bec.devices.falcon_slim.FalconSuperXASSlim deviceConfig: prefix: 'X10DA-SITORO:' enabled: true onFailure: raise - readoutPriority: async + readoutPriority: monitored softwareTrigger: false manip_new_trx: description: Old Sample Manipulator X-Translation @@ -52,3 +52,12 @@ trigger: onFailure: raise readoutPriority: baseline softwareTrigger: true +kb_slit_y: + readoutPriority: baseline + description: KB slit axis Y + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-SV1:OPENY + onFailure: retry + enabled: true + softwareTrigger: false diff --git a/superxas_bec/devices/falcon_slim.py b/superxas_bec/devices/falcon_slim.py index ef597d3..2045eac 100644 --- a/superxas_bec/devices/falcon_slim.py +++ b/superxas_bec/devices/falcon_slim.py @@ -3,7 +3,7 @@ import enum import numpy as np from bec_lib.logger import bec_logger from ophyd import Component as Cpt -from ophyd import Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV, Kind, Signal +from ophyd import Device, EpicsSignal, EpicsSignalRO, Kind, Signal from ophyd_devices import CompareStatus, DeviceStatus, PreviewSignal, StatusBase from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase @@ -20,15 +20,15 @@ class FalconAcquiringStatus(int, enum.Enum): class FalconControlSlim(Device): """Slim Falcon Control class for SuperXAS. prefix: 'X10DA-SITORO:'""" - start = Cpt(EpicsSignal, "EraseStart", doc="XMAP start signal") - stop = Cpt(EpicsSignal, "StopAll", doc="XMAP stop signal") - acquiring = Cpt(EpicsSignalRO, "Acquiring", auto_monitor=True, doc="XMAP acquiring signal") - icr = Cpt(EpicsSignalRO, "dxp1:InputCountRate", doc="XMAP input count rate") - ocr = Cpt(EpicsSignalRO, "dxp1:OutputCountRate", doc="XMAP output count rate") - ert = Cpt(EpicsSignalRO, "mca1.ERTM", doc="XMAP elapsed real time") - roi = Cpt(EpicsSignalRO, "mca1.R0", kind=Kind.hinted, doc="XMAP ROI signal") - label = Cpt(EpicsSignalRO, "mca1.R0NM", kind=Kind.config, doc="XMAP ROI signal") - spectrum_val = Cpt(EpicsSignalRO, "mca1.VAL", kind=Kind.omitted) + erase_start:EpicsSignal = Cpt(EpicsSignal, "EraseStart", kind=Kind.omitted, doc="XMAP start signal") + stop_all:EpicsSignal = Cpt(EpicsSignal, "StopAll", kind=Kind.omitted,doc="XMAP stop signal") + acquiring:EpicsSignalRO = Cpt(EpicsSignalRO, "Acquiring", kind=Kind.omitted, auto_monitor=True, doc="XMAP acquiring signal") + icr:EpicsSignalRO = Cpt(EpicsSignalRO, "dxp1:InputCountRate", doc="XMAP input count rate") + ocr:EpicsSignalRO = Cpt(EpicsSignalRO, "dxp1:OutputCountRate", doc="XMAP output count rate") + ert:EpicsSignalRO = Cpt(EpicsSignalRO, "mca1.ERTM", doc="XMAP elapsed real time") + roi:EpicsSignalRO = Cpt(EpicsSignalRO, "mca1.R0", kind=Kind.hinted, doc="XMAP ROI signal") + label:EpicsSignalRO = Cpt(EpicsSignalRO, "mca1.R0NM", kind=Kind.config, doc="XMAP ROI signal") + spectrum_val:EpicsSignalRO = Cpt(EpicsSignalRO, "mca1.VAL", kind=Kind.omitted) # Preview Signal for Falcon detector spectrum = Cpt( @@ -36,11 +36,11 @@ class FalconControlSlim(Device): ) # Configuration attributes - collect_mode = Cpt(EpicsSignal, "CollectMode", doc="Collect mode signal") - preset_real_time = Cpt(EpicsSignalWithRBV, "PresetRealTime", doc="Preset real time signal") + collect_mode:EpicsSignal = Cpt(EpicsSignal, "CollectMode", doc="Collect mode signal") + preset_real:EpicsSignal = Cpt(EpicsSignal, "PresetReal", doc="Preset real time signal") # Computed signal for dead time corrected counts - dead_cor_roi0_count = Cpt(Signal, "dead_cor_roi0_count", doc="Dead time corrected ROI 0 count") + dead_cor_roi0_count = Cpt(Signal, name="dead_cor_roi0_count", doc="Dead time corrected ROI 0 count") def compute_dead_time_corrected_signal(icr: float, ocr: float, roi: float, ert: float): @@ -106,7 +106,7 @@ class FalconSuperXASSlim(PSIDeviceBase, FalconControlSlim): status.wait(timeout=self._pv_timeout) self.collect_mode.set(0).wait(timeout=self._pv_timeout) - self.preset_real_time.set(0).wait(timeout=self._pv_timeout) + self.preset_real.set(0).wait(timeout=self._pv_timeout) def on_unstage(self) -> DeviceStatus | StatusBase | None: """Called while unstaging the device.""" diff --git a/superxas_bec/devices/trigger.py b/superxas_bec/devices/trigger.py index 1240857..390c39f 100644 --- a/superxas_bec/devices/trigger.py +++ b/superxas_bec/devices/trigger.py @@ -146,7 +146,7 @@ class Trigger(PSIDeviceBase, TriggerControl): falcon.stop_all.put(1) status.wait(timeout=self._pv_timeout) time.sleep(0.4) # Simulate some processing time - falcon.update_data() + falcon.obj.update_data() return status_smpl def on_complete(self) -> DeviceStatus | StatusBase | None: -- 2.49.1 From a650759ae70bd2143378992bd965da5bfda5b505 Mon Sep 17 00:00:00 2001 From: appel_c Date: Thu, 26 Jun 2025 14:03:51 +0200 Subject: [PATCH 33/42] refactor(falcon): cleanup falcon integration, remove extra classes, move scripts. --- bin/test_ad_based_falcon.py | 50 +++ bin/test_falcon.py | 50 +++ superxas_bec/devices/falcon.py | 296 +++++++++--------- superxas_bec/devices/falcon_ad.py | 130 ++++++++ superxas_bec/devices/falcon_direct.py | 276 ---------------- superxas_bec/devices/falcon_slim.py | 150 --------- superxas_bec/devices/test.py | 97 ------ superxas_bec/devices/test_script_cachannel.py | 61 ---- superxas_bec/devices/test_script_ophyd.py | 82 ----- superxas_bec/devices/test_script_pyepics.py | 74 ----- superxas_bec/devices/trigger.py | 5 +- 11 files changed, 380 insertions(+), 891 deletions(-) create mode 100644 bin/test_ad_based_falcon.py create mode 100644 bin/test_falcon.py create mode 100644 superxas_bec/devices/falcon_ad.py delete mode 100644 superxas_bec/devices/falcon_direct.py delete mode 100644 superxas_bec/devices/falcon_slim.py delete mode 100644 superxas_bec/devices/test.py delete mode 100644 superxas_bec/devices/test_script_cachannel.py delete mode 100644 superxas_bec/devices/test_script_ophyd.py delete mode 100644 superxas_bec/devices/test_script_pyepics.py diff --git a/bin/test_ad_based_falcon.py b/bin/test_ad_based_falcon.py new file mode 100644 index 0000000..73066a0 --- /dev/null +++ b/bin/test_ad_based_falcon.py @@ -0,0 +1,50 @@ +import time + +from ophyd_devices import CompareStatus + +from superxas_bec.devices.falcon_ad import FalconAcquiringStatus, FalconAD +from superxas_bec.devices.trigger import SamplingDone, Trigger + +if __name__ == "__main__": + print("Initializing Falcon...") + time_started = time.time() + + falcon = FalconAD(name="test_device", prefix="X10DA-SITORO:", scan_info=None) + trigger = Trigger(name="trigger_device", prefix="X10DA-ES1:", scan_info=None) + + falcon.wait_for_connection(timeout=50, all_signals=True) + trigger.wait_for_connection(timeout=50, all_signals=True) + print(f"Device initialized in {time.time() - time_started:.2f} seconds.") + + if falcon.acquiring.get != FalconAcquiringStatus.DONE: + status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) + falcon.stop_all.put(1) + status.wait(timeout=5) + + # Test loop + for i in range(500): + start_time = time.time() + + status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING) + falcon.erase_start.put(1) + status.wait(timeout=5) + + status2 = CompareStatus(trigger.smpl_done, SamplingDone.DONE) + trigger.smpl.put(1) + status2.wait(timeout=5) + + status3 = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) + falcon.stop_all.put(1) + status3.wait(timeout=5) + + time.sleep(0.1) + + icr = falcon.dxp1.input_count_rate.get() + ocr = falcon.dxp1.output_count_rate.get() + roi = falcon.mca1.rois.count.get() + ert = falcon.mca1.elapsed_real_time.get() + dead_time_corrected_signal = falcon.dead_time_corrected_signal.get() + + print( + f"time={time.time() - start_time:.4f}", icr, ocr, roi, ert, dead_time_corrected_signal + ) diff --git a/bin/test_falcon.py b/bin/test_falcon.py new file mode 100644 index 0000000..32806b0 --- /dev/null +++ b/bin/test_falcon.py @@ -0,0 +1,50 @@ +import time + +from ophyd_devices import CompareStatus + +from superxas_bec.devices.falcon import FalconAcquiringStatus, FalconSuperXAS +from superxas_bec.devices.trigger import SamplingDone, Trigger + +if __name__ == "__main__": + print("Initializing Falcon...") + time_started = time.time() + + falcon = FalconSuperXAS(name="test_device", prefix="X10DA-SITORO:", scan_info=None) + trigger = Trigger(name="trigger_device", prefix="X10DA-ES1:", scan_info=None) + + falcon.wait_for_connection(timeout=50, all_signals=True) + trigger.wait_for_connection(timeout=50, all_signals=True) + print(f"Device initialized in {time.time() - time_started:.2f} seconds.") + + if falcon.acquiring.get != FalconAcquiringStatus.DONE: + status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) + falcon.stop_all.put(1) + status.wait(timeout=5) + + # Test loop + for i in range(500): + start_time = time.time() + + status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING) + falcon.erase_start.put(1) + status.wait(timeout=5) + + status2 = CompareStatus(trigger.smpl_done, SamplingDone.DONE) + trigger.smpl.put(1) + status2.wait(timeout=5) + + status3 = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) + falcon.stop_all.put(1) + status3.wait(timeout=5) + + time.sleep(0.1) + + icr = falcon.icr.get() + ocr = falcon.ocr.get() + roi = falcon.roi.get() + ert = falcon.ert.get() + dead_time_corrected_signal = falcon.dead_time_corrected_signal.get() + + print( + f"time={time.time() - start_time:.4f}", icr, ocr, roi, ert, dead_time_corrected_signal + ) diff --git a/superxas_bec/devices/falcon.py b/superxas_bec/devices/falcon.py index 94b1f02..1287a1a 100644 --- a/superxas_bec/devices/falcon.py +++ b/superxas_bec/devices/falcon.py @@ -1,18 +1,14 @@ -"""FALCON device implementation for SuperXAS""" +"""Module for Falcon detector at SuperXAS.""" import enum -import time +import queue +import threading import numpy as np -from bec_lib.devicemanager import ScanInfo -from bec_lib.endpoints import MessageEndpoints from bec_lib.logger import bec_logger -from bec_lib.messages import DeviceMessage from ophyd import Component as Cpt -from ophyd import DeviceStatus, Kind, Signal, Staged, StatusBase -from ophyd.status import SubscriptionStatus -from ophyd_devices import CompareStatus, TransitionStatus -from ophyd_devices.devices.dxp import EpicsDXPFalcon, EpicsMCARecord, Falcon +from ophyd import Device, EpicsSignal, EpicsSignalRO, Kind, SignalRO +from ophyd_devices import CompareStatus, DeviceStatus, PreviewSignal, StatusBase from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase logger = bec_logger.logger @@ -25,110 +21,137 @@ class FalconAcquiringStatus(int, enum.Enum): ACQUIRING = 1 -def compute_dead_time_corrected_signal(icr: float, ocr: float, roi: float, ert: float): - _dead_time = 1.182e-7 - if icr == 0 or ocr == 0: - return 0 +class DeadTimeCorrectedSignal(SignalRO): + """Signal for dead time corrected counts.""" - # Check that relative change is large enough - test = 1e9 - test_icr = icr - n = 0 - while test > _dead_time and n < 30: - try: - true_icr = icr * np.exp(test_icr * _dead_time) - test = (true_icr - test_icr) / test_icr - test_icr = true_icr - n += 1 - except Exception as e: # pylint: disable=broad-except - logger.info(f"Error in computation of deadtime corrected signal, error: {e}") + def __init__(self, *args, parent: Device | None = None, **kwargs): + super().__init__(*args, **kwargs) + self.parent = parent + self._dead_time = 1.182e-7 # Dead time in seconds + + def get(self, **kwargs) -> float | None: + icr = self.parent.icr.get() + ocr = self.parent.ocr.get() + roi = self.parent.roi.get() + ert = self.parent.ert.get() + return self.compute_deadtime_corrected_signal(icr, ocr, roi, ert) + + def compute_deadtime_corrected_signal( + self, icr: float, ocr: float, roi: float, ert: float + ) -> float: + """Method to compute dead time corrected signal.""" + if icr == 0 or ocr == 0: return 0 - # Return corrected roi counts - cor_roi_cnts = 0 - if ocr * ert != 0: - cor_roi_cnts = roi * true_icr / (ocr * ert) - return cor_roi_cnts + # Check that relative change is large enough + test = 1e9 + test_icr = icr + n = 0 + while test > self._dead_time and n < 30: + try: + true_icr = icr * np.exp(test_icr * self._dead_time) + test = (true_icr - test_icr) / test_icr + test_icr = true_icr + n += 1 + except Exception as e: # pylint: disable=broad-except + logger.info(f"Error in computation of deadtime corrected signal, error: {e}") + return 0 + + # Return corrected roi counts + cor_roi_cnts = 0 + if ocr * ert != 0: + cor_roi_cnts = roi * true_icr / (ocr * ert) + return cor_roi_cnts -class FalconControl(Falcon): - """Falcon Control class at SuperXAS. prefix: 'X10DA-SITORO:'""" +class FalconControl(Device): + """Falcon Control class for SuperXAS. prefix: 'X10DA-SITORO:'""" - # _default_read_attrs = Falcon._default_read_attrs + ( - # "dxp1", - # # # "dxp2", - # "mca1", - # # # "mca2", - # "dead_time_cor_cnts1", - # # # "dead_time_cor_cnts2", - # ) - # _default_configuration_attrs = Falcon._default_configuration_attrs + ( - # "dxp1", - # # "dxp2", - # "mca1", - # # "mca2", - # "dead_time_cor_cnts1", - # # "dead_time_cor_cnts2", - # ) + # PVs for Falcon control + erase_start: EpicsSignal = Cpt( + EpicsSignal, "EraseStart", kind=Kind.omitted, doc="XMAP start signal" + ) + stop_all: EpicsSignal = Cpt(EpicsSignal, "StopAll", kind=Kind.omitted, doc="XMAP stop signal") + acquiring: EpicsSignalRO = Cpt( + EpicsSignalRO, + "Acquiring", + kind=Kind.omitted, + auto_monitor=True, + doc="XMAP acquiring signal", + ) + # PVs for Signals, auto_monitors are active here + icr: EpicsSignalRO = Cpt( + EpicsSignalRO, + "dxp1:InputCountRate", + kind=Kind.normal, + auto_monitor=True, + doc="XMAP input count rate", + ) + ocr: EpicsSignalRO = Cpt( + EpicsSignalRO, + "dxp1:OutputCountRate", + kind=Kind.normal, + auto_monitor=True, + doc="XMAP output count rate", + ) + ert: EpicsSignalRO = Cpt( + EpicsSignalRO, + "mca1.ERTM", + kind=Kind.normal, + auto_monitor=True, + doc="XMAP elapsed real time", + ) + roi: EpicsSignalRO = Cpt( + EpicsSignalRO, "mca1.R0", kind=Kind.normal, auto_monitor=True, doc="XMAP ROI signal" + ) + label: EpicsSignalRO = Cpt(EpicsSignalRO, "mca1.R0NM", kind=Kind.config, doc="XMAP ROI label") + spectrum_val: EpicsSignalRO = Cpt( + EpicsSignalRO, "mca1.VAL", kind=Kind.omitted, doc="XMAP spectrum signal" + ) - # DXP parameters - dxp1 = Cpt(EpicsDXPFalcon, "dxp1:") - # dxp2 = Cpt(EpicsDXPFalcon, "dxp2:") + # Configuration attributes + collect_mode: EpicsSignal = Cpt(EpicsSignal, "CollectMode", doc="Collect mode signal") + preset_real_time: EpicsSignal = Cpt( + EpicsSignal, "PresetRealTime", doc="Preset real time signal" + ) - # MCA record with spectrum data - mca1 = Cpt(EpicsMCARecord, "mca1") - # mca2 = Cpt(EpicsMCARecord, "mca2") + # Preview Signal for Falcon detector + spectrum: PreviewSignal = Cpt( + PreviewSignal, + name="spectrum", + ndim=1, + kind=Kind.omitted, + auto_monitor=True, + doc="Preview signal for Falcon detector spectrum", + ) - # dead_time_cor_cnts2 = Cpt(DeadTimeCorrectedCounts, name='dead_time_cor_cnts', channel=2, kind=Kind.normal) + # Computed signal for dead time corrected counts + dead_cor_roi0_count = Cpt( + DeadTimeCorrectedSignal, + name="dead_cor_roi0_count", + kind=Kind.hinted, + doc="Dead time corrected ROI 0 count", + ) class FalconSuperXAS(PSIDeviceBase, FalconControl): - """Falcon implementierung at SuperXAS. prefix: 'X10DA-SITORO:'""" + """Slim Falcon implementation at SuperXAS. prefix: 'X10DA-SITORO:'""" - _default_read_attrs = ("icr", "ocr", "elap_real_time", "roi0_count", "dead_cor_roi0_count") - _default_configuration_attrs = None - - preview = Cpt( - Signal, name="preview", kind=Kind.omitted, doc="Preview signal for Falcon detector" - ) - icr = Cpt(Signal, name="icr", kind=Kind.normal) - ocr = Cpt(Signal, name="icr", kind=Kind.normal) - elap_real_time = Cpt(Signal, name="icr", kind=Kind.normal) - roi0_count = Cpt(Signal, name="icr", kind=Kind.normal) - dead_cor_roi0_count = Cpt( - Signal, - name="dead_cor_roi0_count", - doc="Async dead time corrected ROI 0 count signal", - kind=Kind.normal, - ) - - ######################################## - # Beamline Specific Implementations # - ######################################## - - def __init__( - self, - name: str, - prefix: str = "", - scan_info: ScanInfo | None = None, - device_manager=None, - **kwargs, - ): - """ - Initialize Falcon device. - - Args: - name (str): Name of the device - prefix (str): Prefix of the device - scan_info (ScanInfo): Information about the scan - **kwargs: Additional keyword arguments - """ + def __init__(self, *, name, prefix="", scan_info=None, device_manager=None, **kwargs): super().__init__( name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs ) - self.device_manager = device_manager - self.mca1.stage_sigs = {} - self._pv_timeout = 5 + self._pv_timeout = 5 # seconds + self._dead_time = 1.182e-7 # Dead time in seconds + self.update_queue: queue.Queue = queue.Queue() + self.current_values: dict = {} + self.update_thread: threading.Thread | None = None + self.threading_event = threading.Event() # Event to control the update thread + self.r_lock: threading.RLock = threading.RLock() # Lock for thread safety + + ######################################### + #### Custom beamline specific methods ### + ######################################### def on_init(self) -> None: """ @@ -138,27 +161,34 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): set default values on signals, please use on_connected instead. """ + def on_destroy(self): + """ + Called when the device is destroyed. + + This method can be used to clean up resources or stop threads. + """ + if self.update_thread and self.update_thread.is_alive(): + self.threading_event.set() + self.update_queue.put((None, None)) # Signal the thread to stop + self.update_thread.join(timeout=5) # Wait for the thread to finish + if self.update_thread.is_alive(): + logger.warning(f"Update thread for device {self.name} did not finish in 5s.") + self.update_thread = None + def on_connected(self) -> None: """ - Called after the device is connected and its signals are connected. - Default values for signals should be set here. + Upon being connected, make sure the Falcon is not acquiring. """ - - def stage(self) -> list[object] | DeviceStatus | StatusBase: # type: ignore - """Stage the device.""" - if self.staged != Staged.no: - return [self] - self.stopped = False - status = self.on_stage() # pylint: disable=assignment-from-no-return - if isinstance(status, StatusBase): - return status - return [self] + status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) + self.cancel_on_stop(status) + self.stop_all.put(1) + status.wait(timeout=self._pv_timeout) def on_stage(self) -> DeviceStatus | StatusBase | None: """ Called while staging the device. - Information about the upcoming scan can be accessed from the scan_info (self.scan_info.msg) object. + Information about the upcoming scan can be accessed self.scan_info.msg. """ if self.acquiring.get() != FalconAcquiringStatus.DONE: logger.info(f"Falcon state was {self.acquiring.get()} during stage. Calling stop_all") @@ -167,15 +197,20 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): self.stop_all.put(1) status.wait(timeout=self._pv_timeout) - self.collect_mode.set(0).wait(timeout=self._pv_timeout) - self.preset_real_time.set(0).wait(timeout=self._pv_timeout) + status_list = [] + status_list.append(self.collect_mode.set(0)) + status_list.append(self.preset_real_time.set(0)) + for status in status_list: + self.cancel_on_stop(status) + for status in status_list: + status.wait(timeout=self._pv_timeout) def on_unstage(self) -> DeviceStatus | StatusBase | None: """Called while unstaging the device.""" if self.acquiring.get() != FalconAcquiringStatus.DONE: - self.stop_all.put(1) status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) self.cancel_on_stop(status) + self.stop_all.put(1) return status def on_pre_scan(self) -> DeviceStatus | StatusBase | None: @@ -193,38 +228,3 @@ class FalconSuperXAS(PSIDeviceBase, FalconControl): def on_stop(self) -> None: """Called when the device is stopped.""" self.stop_all.put(1) - - def send_data(self): - """ - Wait until the Falcon is done acquiring data. - - This method blocks until the acquiring status is DONE. - """ - time_started = time.time() - # CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) - logger.info(f"Sending data for {self.name} at {time_started}") - icr = self.dxp1.input_count_rate.get() - ocr = self.dxp1.output_count_rate.get() - roi = self.mca1.rois.count.get() - ert = self.mca1.elapsed_real_time.get() - self.icr.put(icr) - logger.info(f"Data to plot {self.icr.get()}") - self.ocr.put(ocr) - self.elap_real_time.put(ert) - self.roi0_count.put(roi) - self.dead_cor_roi0_count.put(compute_dead_time_corrected_signal(icr, ocr, roi, ert)) - # self._send_preview_async() - logger.info(f"Data sent for {self.name} at {time.time()- time_started}") - - def _send_preview_async(self) -> None: - metadata = {"async_update": {"type": "add", "max_shape": [None, 3000]}} - data = {self.preview.name: {"value": self.mca1.spectrum.get(), "timestamp": time.time()}} - msg = DeviceMessage(signals=data, metadata=metadata) - self.device_manager.connector.xadd( - MessageEndpoints.device_async_readback( - scan_id=self.scan_info.msg.scan_id, device=self.name - ), - msg_dict={"data": msg}, - max_size=1000, - expire=900, - ) diff --git a/superxas_bec/devices/falcon_ad.py b/superxas_bec/devices/falcon_ad.py new file mode 100644 index 0000000..5061dd3 --- /dev/null +++ b/superxas_bec/devices/falcon_ad.py @@ -0,0 +1,130 @@ +"""FALCON device implementation for SuperXAS""" + +import enum + +from bec_lib.devicemanager import ScanInfo +from bec_lib.logger import bec_logger +from ophyd import Component as Cpt +from ophyd import DeviceStatus, Staged, StatusBase +from ophyd_devices import CompareStatus +from ophyd_devices.devices.dxp import EpicsDXPFalcon, EpicsMCARecord, Falcon +from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase + +logger = bec_logger.logger + + +class FalconAcquiringStatus(int, enum.Enum): + """Status of Falcon""" + + DONE = 0 + ACQUIRING = 1 + + +class FalconADControl(Falcon): + """Falcon Control class at SuperXAS. prefix: 'X10DA-SITORO:'""" + + # DXP parameters + dxp1 = Cpt(EpicsDXPFalcon, "dxp1:") + + # MCA record with spectrum data + mca1 = Cpt(EpicsMCARecord, "mca1") + + +class FalconAD(PSIDeviceBase, FalconADControl): + """Falcon implementierung at SuperXAS. prefix: 'X10DA-SITORO:'""" + + ######################################## + # Beamline Specific Implementations # + ######################################## + + def __init__( + self, + name: str, + prefix: str = "", + scan_info: ScanInfo | None = None, + device_manager=None, + **kwargs, + ): + """ + Initialize Falcon device. + + Args: + name (str): Name of the device + prefix (str): Prefix of the device + scan_info (ScanInfo): Information about the scan + **kwargs: Additional keyword arguments + """ + super().__init__( + name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs + ) + self.device_manager = device_manager + self._pv_timeout = 5 + + 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 = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) + self.cancel_on_stop(status) + self.stop_all.put(1) + status.wait(timeout=self._pv_timeout) + + # TODO Skip AD stage command (trigger_value) for now! + def stage(self) -> list[object] | DeviceStatus | StatusBase: # type: ignore + """Stage the device.""" + if self.staged != Staged.no: + return [self] + self.stopped = False + status = self.on_stage() # pylint: disable=assignment-from-no-return + if isinstance(status, StatusBase): + return status + return [self] + + def on_stage(self) -> DeviceStatus | StatusBase | None: + """ + Called while staging the device. + + Information about the upcoming scan can be accessed from the scan_info (self.scan_info.msg) object. + """ + if self.acquiring.get() != FalconAcquiringStatus.DONE: + logger.info(f"Falcon state was {self.acquiring.get()} during stage. Calling stop_all") + status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) + self.cancel_on_stop(status) + self.stop_all.put(1) + status.wait(timeout=self._pv_timeout) + + self.collect_mode.set(0).wait(timeout=self._pv_timeout) + self.preset_real_time.set(0).wait(timeout=self._pv_timeout) + + def on_unstage(self) -> DeviceStatus | StatusBase | None: + """Called while unstaging the device.""" + if self.acquiring.get() != FalconAcquiringStatus.DONE: + self.stop_all.put(1) + status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) + self.cancel_on_stop(status) + return status + + def on_pre_scan(self) -> DeviceStatus | StatusBase | None: + """Called right before the scan starts on all devices automatically.""" + + def on_trigger(self) -> DeviceStatus | StatusBase | None: + """Called when the device is triggered.""" + + def on_complete(self) -> DeviceStatus | StatusBase | None: + """Called to inquire if a device has completed a scans.""" + + def on_kickoff(self) -> DeviceStatus | StatusBase | 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.stop_all.put(1) diff --git a/superxas_bec/devices/falcon_direct.py b/superxas_bec/devices/falcon_direct.py deleted file mode 100644 index 9707e8b..0000000 --- a/superxas_bec/devices/falcon_direct.py +++ /dev/null @@ -1,276 +0,0 @@ -import enum -import time -from collections import OrderedDict -from typing import TYPE_CHECKING - -import numpy as np -from bec_lib.devicemanager import ScanInfo -from bec_lib.endpoints import MessageEndpoints -from bec_lib.logger import bec_logger -from bec_lib.messages import DeviceMessage -from bec_server.device_server.devices.devicemanager import DeviceManagerDS -from ophyd import Component as Cpt -from ophyd import Device -from ophyd import DynamicDeviceComponent as DDC -from ophyd import EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV, Kind, Signal -from ophyd_devices import ( - AsyncSignal, - CompareStatus, - DeviceStatus, - PreviewSignal, - StatusBase, - TransitionStatus, -) -from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase - -logger = bec_logger.logger - - -class FalconAcquiringStatus(int, enum.Enum): - """Status of Falcon""" - - DONE = 0 - ACQUIRING = 1 - - -class DXPControl(Device): - """DXP Control Device for Falcon detector""" - - input_count_rate = Cpt(EpicsSignalRO, "InputCountRate", kind=Kind.omitted) - output_count_rate = Cpt(EpicsSignalRO, "OutputCountRate", kind=Kind.omitted) - elapsed_real_time = Cpt(EpicsSignalRO, "ElapsedRealTime", kind=Kind.omitted) - - -class MCAControl(Device): - """MCA Control Device for Falcon detector""" - - spectrum = Cpt(EpicsSignalRO, ".VAL", kind=Kind.omitted) - roi0_count = Cpt(EpicsSignalRO, ".R0", kind=Kind.omitted) - roi0_label = Cpt(EpicsSignal, ".R0NM", kind=Kind.omitted) - elapsed_real_time = Cpt(EpicsSignalRO, ".ERTM", kind=Kind.omitted) - - -class DeadTimeCorrectedCounts(Signal): - """Signal to calculate dead time corrected counts""" - - def __init__(self, name: str, channel: int, **kwargs): - """ - Initialize DeadTimeCorrectedCounts signal. - - Args: - name (str): Name of the signal - channel (int): Channel number - """ - super().__init__(name=name, **kwargs) - self._channel = channel - self._dead_time = 1.182e-7 - - # pylint: disable=arguments-differ - def get(self) -> float: - """Get dead time corrected counts base on signals from dxp and mca of Falcon""" - dxp: DXPControl = getattr(self.parent, f"dxp{self._channel}") - mca: MCAControl = getattr(self.parent, f"mca{self._channel}") - - icr = dxp.input_count_rate.get() - ocr = dxp.output_count_rate.get() - roi = mca.roi_count.get() - ert = mca.elapsed_real_time.get() - # print(icr, ocr, roi, ert) - - if icr == 0 or ocr == 0: - return 0 - - # Check that relative change is large enough - test = 1e9 - test_icr = icr - n = 0 - while test > self._dead_time and n < 30: - try: - true_icr = icr * np.exp(test_icr * self._dead_time) - test = (true_icr - test_icr) / test_icr - test_icr = true_icr - n += 1 - except Exception as e: # pylint: disable=broad-except - logger.info(f"Error in computation of signal {self.name}, error: {e}") - return 0 - - # Return corrected roi counts - cor_roi_cnts = 0 - if ocr * ert != 0: - cor_roi_cnts = roi * true_icr / (ocr * ert) - return cor_roi_cnts - - -class FalconControlDirect(Device): - """Stripped implementation of Falcon detector for SuperXAS; prefix: 'X10DA-SITORO:'""" - - # Acquisition control - erase_all = Cpt(EpicsSignal, "EraseAll", kind=Kind.omitted) - erase_start = Cpt(EpicsSignal, "EraseStart", put_complete=True, kind=Kind.omitted) - start_all = Cpt(EpicsSignal, "StartAll", put_complete=True, kind=Kind.omitted) - stop_all = Cpt(EpicsSignal, "StopAll", kind=Kind.omitted) - - collect_mode = Cpt(EpicsSignalWithRBV, "CollectMode", kind=Kind.config) - preset_real_time = Cpt(EpicsSignal, "PresetReal", kind=Kind.config) - acquiring = Cpt(EpicsSignal, "Acquiring", kind=Kind.omitted, auto_monitor=True) - - # DXP parameters - dxp1 = Cpt(DXPControl, "dxp1:", kind=Kind.omitted) - # dxp2 = Cpt(EpicsDXPFalcon, "dxp2:") - - # MCA record with spectrum data - mca1 = Cpt(MCAControl, "mca1", kind=Kind.omitted) - # mca2 = Cpt(EpicsMCARecord, "mca2") - - # Norm Signal - dead_time_cor_cnts1 = Cpt( - DeadTimeCorrectedCounts, name="dead_time_cor_cnts", channel=1, kind=Kind.omitted - ) - # dead_time_cor_cnts2 = Cpt(DeadTimeCorrectedCounts, name='dead_time_cor_cnts', channel=2, kind=Kind.normal) - - -class FalconSuperXASDirect(PSIDeviceBase, FalconControlDirect): - """Falcon implementierung at SuperXAS. prefix: 'X10DA-SITORO:'""" - - preview = Cpt( - Signal, name="preview", kind=Kind.omitted, doc="Preview signal for Falcon detector" - ) - icr = Cpt( - Signal, name="icr", kind=Kind.normal) - ocr = Cpt(Signal, name="icr", kind=Kind.normal) - elap_real_time = Cpt(Signal, name="icr", kind=Kind.normal) - roi0_count = Cpt(Signal, name="icr", kind=Kind.normal) - dead_cor_roi0_count = Cpt( - Signal, - name="dead_cor_roi0_count", - doc="Async dead time corrected ROI 0 count signal", - kind=Kind.normal - ) - - ######################################## - # Beamline Specific Implementations # - ######################################## - - def __init__( - self, - name: str, - prefix: str = "", - scan_info: ScanInfo | None = None, - device_manager: DeviceManagerDS | None = None, - **kwargs, - ): - """ - Initialize Falcon device. - - Args: - name (str): Name of the device - prefix (str): Prefix of the device - scan_info (ScanInfo): Information about the scan - **kwargs: Additional keyword arguments - """ - super().__init__( - name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs - ) - self.dm = device_manager - self._pv_timeout = 5 - self._async_read_data = [ - "dxp_input_count_rate", - "dxp_output_count_rate", - "mca_elapsed_real_time", - "mca_rois_roi0_count", - ] - self._index = 0 - - 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. - """ - # self.stop_all.put(1) - # CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(5) - # self.mca1.spectrum.subscribe(self._update_preview, run=False) - - # TODO add again once PreviewSIgnal works with GUI - # def _update_preview(self, old_value, value, **kwargs) -> None: - # """Update the preview signal with the latest spectrum data.""" - # logger.info(f"Received new spectrum data: {value}") - # self.preview.put(value) - - def on_stage(self) -> DeviceStatus | StatusBase | 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_all.put(1, use_complete=True) - if self.acquiring.get() == FalconAcquiringStatus.ACQUIRING: - status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) - self.stop_all.set(1).wait(self._pv_timeout) - try: - status.wait(self._pv_timeout) - except Exception as exc: - logger.warning(f"Device {self.name} failed to reach state 'done', current state {FalconAcquiringStatus(self.acquiring.get())}") - - self.collect_mode.set(0).wait() - self.preset_real_time.set(0).wait() - self._index = 0 - - def on_unstage(self) -> DeviceStatus | StatusBase | None: - """Called while unstaging the device.""" - self.stop_all.set(1).wait(self._pv_timeout) - - def on_pre_scan(self) -> DeviceStatus | StatusBase | None: - """Called right before the scan starts on all devices automatically.""" - - def on_trigger(self) -> DeviceStatus | StatusBase | None: - """Called when the device is triggered.""" - - def on_complete(self) -> DeviceStatus | StatusBase | None: - """Called to inquire if a device has completed a scans.""" - - def on_kickoff(self) -> DeviceStatus | StatusBase | 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.stop_all.set(1).wait(self._pv_timeout) - - def send_data(self): - """ - Wait until the Falcon is done acquiring data. - - This method blocks until the acquiring status is DONE. - """ - time_started = time.time() - # CompareStatus(self.acquiring, FalconAcquiringStatus.DONE).wait(self._pv_timeout) - logger.info(f"Sending data for {self.name} at {time_started}") - self.icr.put(self.dxp1.input_count_rate.get()) - logger.info(f"Data to plot {self.icr.get()}") - self.ocr.put(self.dxp1.output_count_rate.get()) - self.elap_real_time.put(self.mca1.elapsed_real_time.get()) - self.roi0_count.put(self.mca1.roi_count.get()) - # self.dead_cor_roi0_count.put(self.dead_time_cor_cnts1.get()) - self._send_preview_async() - logger.info(f"Data sent for {self.name} at {time.time()- time_started}") - - def _send_preview_async(self) -> None: - metadata = {"async_update": {"type": "add", "max_shape": [None, 3000]}} - data = {self.preview.name: {"value": self.mca1.spectrum.get(), "timestamp": time.time()}} - msg = DeviceMessage(signals=data, metadata=metadata) - self.dm.connector.xadd( - MessageEndpoints.device_async_readback( - scan_id=self.scan_info.msg.scan_id, device=self.name - ), - msg_dict={"data": msg}, - max_size=1000, - expire=900, - ) - self._index += 1 diff --git a/superxas_bec/devices/falcon_slim.py b/superxas_bec/devices/falcon_slim.py deleted file mode 100644 index 2045eac..0000000 --- a/superxas_bec/devices/falcon_slim.py +++ /dev/null @@ -1,150 +0,0 @@ -import enum - -import numpy as np -from bec_lib.logger import bec_logger -from ophyd import Component as Cpt -from ophyd import Device, EpicsSignal, EpicsSignalRO, Kind, Signal -from ophyd_devices import CompareStatus, DeviceStatus, PreviewSignal, StatusBase -from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase - -logger = bec_logger.logger - - -class FalconAcquiringStatus(int, enum.Enum): - """Status of Falcon""" - - DONE = 0 - ACQUIRING = 1 - - -class FalconControlSlim(Device): - """Slim Falcon Control class for SuperXAS. prefix: 'X10DA-SITORO:'""" - - erase_start:EpicsSignal = Cpt(EpicsSignal, "EraseStart", kind=Kind.omitted, doc="XMAP start signal") - stop_all:EpicsSignal = Cpt(EpicsSignal, "StopAll", kind=Kind.omitted,doc="XMAP stop signal") - acquiring:EpicsSignalRO = Cpt(EpicsSignalRO, "Acquiring", kind=Kind.omitted, auto_monitor=True, doc="XMAP acquiring signal") - icr:EpicsSignalRO = Cpt(EpicsSignalRO, "dxp1:InputCountRate", doc="XMAP input count rate") - ocr:EpicsSignalRO = Cpt(EpicsSignalRO, "dxp1:OutputCountRate", doc="XMAP output count rate") - ert:EpicsSignalRO = Cpt(EpicsSignalRO, "mca1.ERTM", doc="XMAP elapsed real time") - roi:EpicsSignalRO = Cpt(EpicsSignalRO, "mca1.R0", kind=Kind.hinted, doc="XMAP ROI signal") - label:EpicsSignalRO = Cpt(EpicsSignalRO, "mca1.R0NM", kind=Kind.config, doc="XMAP ROI signal") - spectrum_val:EpicsSignalRO = Cpt(EpicsSignalRO, "mca1.VAL", kind=Kind.omitted) - - # Preview Signal for Falcon detector - spectrum = Cpt( - PreviewSignal, name="spectrum", ndim=1, doc="Preview signal for Falcon detector spectrum" - ) - - # Configuration attributes - collect_mode:EpicsSignal = Cpt(EpicsSignal, "CollectMode", doc="Collect mode signal") - preset_real:EpicsSignal = Cpt(EpicsSignal, "PresetReal", doc="Preset real time signal") - - # Computed signal for dead time corrected counts - dead_cor_roi0_count = Cpt(Signal, name="dead_cor_roi0_count", doc="Dead time corrected ROI 0 count") - - -def compute_dead_time_corrected_signal(icr: float, ocr: float, roi: float, ert: float): - _dead_time = 1.182e-7 - if icr == 0 or ocr == 0: - return 0 - - # Check that relative change is large enough - test = 1e9 - test_icr = icr - n = 0 - while test > _dead_time and n < 30: - try: - true_icr = icr * np.exp(test_icr * _dead_time) - test = (true_icr - test_icr) / test_icr - test_icr = true_icr - n += 1 - except Exception as e: # pylint: disable=broad-except - logger.info(f"Error in computation of deadtime corrected signal, error: {e}") - return 0 - - # Return corrected roi counts - cor_roi_cnts = 0 - if ocr * ert != 0: - cor_roi_cnts = roi * true_icr / (ocr * ert) - return cor_roi_cnts - - -class FalconSuperXASSlim(PSIDeviceBase, FalconControlSlim): - """Slim Falcon implementation at SuperXAS. prefix: 'X10DA-SITORO:'""" - - def __init__(self, *, name, prefix="", scan_info=None, device_manager=None, **kwargs): - super().__init__( - name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs - ) - self._pv_timeout = 5 # seconds - - 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. - """ - - def on_stage(self) -> DeviceStatus | StatusBase | None: - """ - Called while staging the device. - - Information about the upcoming scan can be accessed from the scan_info (self.scan_info.msg) object. - """ - if self.acquiring.get() != FalconAcquiringStatus.DONE: - logger.info(f"Falcon state was {self.acquiring.get()} during stage. Calling stop_all") - status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) - self.cancel_on_stop(status) - self.stop_all.put(1) - status.wait(timeout=self._pv_timeout) - - self.collect_mode.set(0).wait(timeout=self._pv_timeout) - self.preset_real.set(0).wait(timeout=self._pv_timeout) - - def on_unstage(self) -> DeviceStatus | StatusBase | None: - """Called while unstaging the device.""" - if self.acquiring.get() != FalconAcquiringStatus.DONE: - self.stop_all.put(1) - status = CompareStatus(self.acquiring, FalconAcquiringStatus.DONE) - self.cancel_on_stop(status) - return status - - def on_pre_scan(self) -> DeviceStatus | StatusBase | None: - """Called right before the scan starts on all devices automatically.""" - - def on_trigger(self) -> DeviceStatus | StatusBase | None: - """Called when the device is triggered.""" - - def on_complete(self) -> DeviceStatus | StatusBase | None: - """Called to inquire if a device has completed a scans.""" - - def on_kickoff(self) -> DeviceStatus | StatusBase | 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.stop_all.put(1) - - def update_data(self): - """ - Set the dead time corrected signal based on input count rate, output count rate, ROI count, and elapsed real time. - - Parameters: - icr (float): Input count rate. - ocr (float): Output count rate. - roi (float): ROI count. - ert (float): Elapsed real time. - """ - - dead_time_corrected_signal = compute_dead_time_corrected_signal( - self.icr.get(), self.ocr.get(), self.roi.get(), self.ert.get() - ) - self.dead_cor_roi0_count.put(dead_time_corrected_signal) - self.spectrum.put(self.spectrum_val.get()) diff --git a/superxas_bec/devices/test.py b/superxas_bec/devices/test.py deleted file mode 100644 index 1f8f872..0000000 --- a/superxas_bec/devices/test.py +++ /dev/null @@ -1,97 +0,0 @@ -import time -import epics - -import numpy as np -from ophyd_devices import CompareStatus, TransitionStatus - -from superxas_bec.devices.falcon_direct import FalconAcquiringStatus, FalconControlDirect as FalconSuperXAS -from superxas_bec.devices.trigger import ContinuousSamplingMode, SamplingDone, Trigger - - -def mock_motor_move(pos: float) -> None: - """Mock function to simulate motor movement.""" - print(f"Mock Motor starts moving...") - # time.sleep(0.5) - print(f"Mock Motor has reached the target position {pos}") - -def sleep_poll(total_sleep:float): - sleep_timer = 0.01 - # time.sleep(total_sleep) - for ii in range(int(total_sleep/sleep_timer)): - time.sleep(sleep_timer) - epics.poll() - -if __name__ == "__main__": - # time.sleep(20) # Give time to connect pyspy - # Exposure time 0.6s - exp_time = 0.6 - # steps = 10 - positions = np.linspace(0, 1, 10) # Simulated positions for the motor - - # Example usage of the FalconSuperXAS and Trigger classes - falcon = FalconSuperXAS(name="falcon", prefix="X10DA-SITORO:") - trigger = Trigger(name="trigger", prefix="X10DA-ES1:") - print(f"Initialized {falcon.name} with prefix {falcon.prefix}") - print(f"Initialized {trigger.name} with prefix {trigger.prefix}") - falcon.wait_for_connection(all_signals=True, timeout=60) - trigger.wait_for_connection(all_signals=True, timeout=60) - - # Simulate a scan! - for iteration in range(3): - print(f"\nStarting iteration {iteration + 1} of the scan...") - - #### STAGE #### - # Check if Falcon is acquiring data and stop if necessary - if falcon.acquiring.get() == FalconAcquiringStatus.ACQUIRING: - print("Falcon is currently acquiring data.") - print("Stopping all acquisitions...") - status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) - falcon.stop_all.put(1) - status.wait(timeout=5) - print("All acquisitions stopped.") - - # Prepare the devices - falcon.collect_mode.set(0).wait() - falcon.preset_real_time.set(0).wait() - print("Falcon is prepared for the scan.") - trigger.start_csmpl.set(ContinuousSamplingMode.OFF).wait(timeout=5) - cycles = max(int(exp_time * 5), 1) # Must be at least 1 cycle, each cycle is 0.2s - trigger.total_cycles.set(cycles).wait(timeout=5) - print(f"Trigger is prepared for the scan with {cycles} cycles.") - #### End STAGE #### - - ### Trigger at each point### - # Simulate motion and data acquisition at each point - print("Starting the scan...") - for pos in positions: - mock_motor_move(pos) - status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING) - falcon.erase_start.put(1) - print(f"Acquiring state after erase_start: {FalconAcquiringStatus(falcon.acquiring.get())}") - status.wait(timeout=5) - sleep_poll(0.4) - # time.sleep(0.4) - status_smpl = TransitionStatus( - trigger.smpl_done, [SamplingDone.RUNNING, SamplingDone.DONE] - ) - trigger.smpl.put(1) - status_smpl.wait() - sleep_poll(0.4) - # time.sleep(0.4) - status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) - falcon.stop_all.put(1) - status.wait(timeout=5) - sleep_poll(0.4) - # time.sleep(0.4) # Simulate some processing time - # print(falcon.mca1.rois.roi0.count.get()) - print(falcon.mca1.roi0_count.get()) - print(falcon.mca1.elapsed_real_time.get()) - print(falcon.dxp1.input_count_rate.get()) - print(falcon.dxp1.output_count_rate.get()) - # print(falcon.mca1.elapsed_real_time.get()) - # print(falcon.max_elapsed_live.get()) - # print(falcon.max_elapsed_real.get()) - sleep_poll(2) - # time.sleep(2) # #FIXME <- When removed, crashes always in second loop! Otherwise, it soemtimes works.. - - diff --git a/superxas_bec/devices/test_script_cachannel.py b/superxas_bec/devices/test_script_cachannel.py deleted file mode 100644 index 77e000e..0000000 --- a/superxas_bec/devices/test_script_cachannel.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/python -# python script - -import time - -from CaChannel import CaChannel, ca - -I0 = CaChannel() -I0.searchw("X10DA-ES1-SAI_01:MEAN") - -Trigger = CaChannel() -Trigger.searchw("X10DA-ES1:SMPL") -TriggerDone = CaChannel() -TriggerDone.searchw("X10DA-ES1:SMPL-DONE") - -XMAPStart = CaChannel() -XMAPStart.searchw("X10DA-SITORO:EraseStart") -XMAPStop = CaChannel() -XMAPStop.searchw("X10DA-SITORO:StopAll") -XMAPAcquiring = CaChannel() -XMAPAcquiring.searchw("X10DA-SITORO:Acquiring") -XMAPICR = CaChannel() -XMAPICR.searchw("X10DA-SITORO:dxp1:InputCountRate") -XMAPOCR = CaChannel() -XMAPOCR.searchw("X10DA-SITORO:dxp1:OutputCountRate") -XMAPROI = CaChannel() -XMAPROI.searchw("X10DA-SITORO:mca1.R0") - - -for i in range(25): - start_time = time.time() - XMAPStart.putw(1) - - f = 0 - while f == 0: - time.sleep(0.05) - f = XMAPAcquiring.getw() - - Trigger.putw(1) - time.sleep(0.2) - - t = 0 - while t == 0: - time.sleep(0.05) - t = TriggerDone.getw() - - XMAPStop.putw(1) - - f = 1 - while f == 1: - time.sleep(0.05) - f = XMAPAcquiring.getw() - - time.sleep(0.1) - - i0 = I0.getw() - icr = XMAPICR.getw() - ocr = XMAPOCR.getw() - roi = XMAPROI.getw() - end_time = time.time() - print(f"time={end_time-start_time:.4f}", i0, icr, ocr, roi) diff --git a/superxas_bec/devices/test_script_ophyd.py b/superxas_bec/devices/test_script_ophyd.py deleted file mode 100644 index bfb7646..0000000 --- a/superxas_bec/devices/test_script_ophyd.py +++ /dev/null @@ -1,82 +0,0 @@ -import time - -from ophyd import Component as Cpt -from ophyd import Device, EpicsSignal, EpicsSignalRO -from ophyd.status import SubscriptionStatus -from ophyd_devices import CompareStatus, TransitionStatus - - -class DummyDevice(Device): - """ - A dummy device for testing purposes. - """ - - i_0 = Cpt(EpicsSignalRO, "ES1-SAI_01:MEAN", doc="I0 signal") - trigger_smpl = Cpt(EpicsSignal, "ES1:SMPL", doc="Trigger signal") - trigger_done = Cpt(EpicsSignalRO, "ES1:SMPL-DONE", auto_monitor=True, doc="Trigger done signal") - - xmap_start = Cpt(EpicsSignal, "SITORO:EraseStart", doc="XMAP start signal") - xmap_stop = Cpt(EpicsSignal, "SITORO:StopAll", doc="XMAP stop signal") - xmap_acquiring = Cpt( - EpicsSignalRO, "SITORO:Acquiring", auto_monitor=True, doc="XMAP acquiring signal" - ) - xmap_icr = Cpt(EpicsSignalRO, "SITORO:dxp1:InputCountRate", doc="XMAP input count rate") - xmap_ocr = Cpt(EpicsSignalRO, "SITORO:dxp1:OutputCountRate", doc="XMAP output count rate") - xmap_roi = Cpt(EpicsSignalRO, "SITORO:mca1.R0", doc="XMAP ROI signal") - - -def state_changed_callback(*, value, old_value, **kwargs): - """Callback for acquiring signal changes.""" - if old_value == 0 and value == 1 or old_value == 1 and value == 0: - print(f"State changed: {old_value} -> {value}") - return True - return False - - -def acquire_stoped(*, value, old_value, **kwargs): - """Callback for acquisition stop.""" - if value == 0: - print("Acquisition stopped.") - return True - return False - - -if __name__ == "__main__": - # Create an instance of the dummy device - print("Initializing DummyDevice...") - time_started = time.time() - - device = DummyDevice(name="test_device", prefix="X10DA-") - - device.wait_for_connection(timeout=50, all_signals=True) - print(f"Device initialized in {time.time() - time_started:.2f} seconds.") - # status = SubscriptionStatus(device.xmap_acquiring, acquire_stoped) - status = CompareStatus(device.xmap_acquiring, 0) - device.xmap_stop.put(1) - status.wait(timeout=5) - - for i in range(500): - start_time = time.time() - # status = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) - status = CompareStatus(device.xmap_acquiring, 1) - device.xmap_start.put(1) - status.wait(timeout=5) - - # status2 = SubscriptionStatus(device.trigger_done, state_changed_callback) - status2 = CompareStatus(device.trigger_done, 0) - device.trigger_smpl.put(1) - status2.wait(timeout=5) - - # status3 = SubscriptionStatus(device.xmap_acquiring, state_changed_callback) - status3 = CompareStatus(device.xmap_acquiring, 0) - device.xmap_stop.put(1) - status3.wait(timeout=5) - - time.sleep(0.1) - - i0 = device.i_0.get() - icr = device.xmap_icr.get() - ocr = device.xmap_ocr.get() - roi = device.xmap_roi.get() - - print(f"time={time.time() - start_time:.4f}", i0, icr, ocr, roi) diff --git a/superxas_bec/devices/test_script_pyepics.py b/superxas_bec/devices/test_script_pyepics.py deleted file mode 100644 index c348f6d..0000000 --- a/superxas_bec/devices/test_script_pyepics.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/python -# PyEpics version of the script - -import time -import threading - -import epics - -def main(): - # Define PVs - I0 = epics.PV("X10DA-ES1-SAI_01:MEAN") - Trigger = epics.PV("X10DA-ES1:SMPL") - TriggerDone = epics.PV("X10DA-ES1:SMPL-DONE", auto_monitor=True) - - XMAPStart = epics.PV("X10DA-SITORO:EraseStart") - XMAPStop = epics.PV("X10DA-SITORO:StopAll") - XMAPAcquiring = epics.PV("X10DA-SITORO:Acquiring", auto_monitor=True) - XMAPICR = epics.PV("X10DA-SITORO:dxp1:InputCountRate") - XMAPOCR = epics.PV("X10DA-SITORO:dxp1:OutputCountRate") - XMAPROI = epics.PV("X10DA-SITORO:mca1.R0") - - - acquire_started = threading.Event() - acquire_stopped = threading.Event() - trigger_done = threading.Event() - - def acquiring_cb(pvname=None, value=None, **kwargs): - if value == 1: - acquire_started.set() - elif value == 0: - acquire_stopped.set() - - def trigger_done_cb(pvname=None, value=None, **kwargs): - if value == 1: - trigger_done.set() - - # Wait for connections (optional) - for pv in [I0, Trigger, TriggerDone, XMAPStart, XMAPStop, XMAPAcquiring, XMAPICR, XMAPOCR, XMAPROI]: - pv.wait_for_connection(timeout=2) - - TriggerDone.add_callback(trigger_done_cb) - XMAPAcquiring.add_callback(acquiring_cb) - - # Measurement loop - for i in range(500): - acquire_started.clear() - acquire_stopped.clear() - trigger_done.clear() - - start_time = time.time() - XMAPStart.put(1, wait=False) - - acquire_started.wait(timeout=5) - - Trigger.put(1, wait=False) - - trigger_done.wait(3) - - XMAPStop.put(1, wait=True) - - acquire_stopped.wait(5) - - time.sleep(0.1) - - i0 = I0.get() - icr = XMAPICR.get() - ocr = XMAPOCR.get() - roi = XMAPROI.get() - end_time = time.time() - - print(f"time={end_time - start_time:.4f}", i0, icr, ocr, roi) - -if __name__=="__main__": - main() diff --git a/superxas_bec/devices/trigger.py b/superxas_bec/devices/trigger.py index 390c39f..a31bd55 100644 --- a/superxas_bec/devices/trigger.py +++ b/superxas_bec/devices/trigger.py @@ -10,7 +10,7 @@ from ophyd import Device, DeviceStatus, EpicsSignal, EpicsSignalRO, Kind, Status from ophyd_devices import CompareStatus, TransitionStatus from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase -from superxas_bec.devices.falcon_slim import FalconAcquiringStatus, FalconSuperXASSlim +from superxas_bec.devices.falcon import FalconAcquiringStatus, FalconSuperXAS logger = bec_logger.logger @@ -127,7 +127,7 @@ class Trigger(PSIDeviceBase, TriggerControl): falcon = self.device_manager.devices.get("falcon", None) if falcon is not None and falcon.enabled is True: - falcon: FalconSuperXASSlim + falcon: FalconSuperXAS status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING) self.cancel_on_stop(status) falcon.erase_start.put(1) @@ -146,7 +146,6 @@ class Trigger(PSIDeviceBase, TriggerControl): falcon.stop_all.put(1) status.wait(timeout=self._pv_timeout) time.sleep(0.4) # Simulate some processing time - falcon.obj.update_data() return status_smpl def on_complete(self) -> DeviceStatus | StatusBase | None: -- 2.49.1 From 3e2ef6decd923834f4268650fb00ff97b040a4e2 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Fri, 27 Jun 2025 10:27:16 +0200 Subject: [PATCH 34/42] refactor(configs): update configs --- superxas_bec/device_configs/x10da_config_250616.yaml | 4 ++-- superxas_bec/device_configs/x10da_config_falcon_test.yaml | 2 +- superxas_bec/device_configs/x10da_config_test.yaml | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/superxas_bec/device_configs/x10da_config_250616.yaml b/superxas_bec/device_configs/x10da_config_250616.yaml index 5a98254..2e174b7 100644 --- a/superxas_bec/device_configs/x10da_config_250616.yaml +++ b/superxas_bec/device_configs/x10da_config_250616.yaml @@ -146,12 +146,12 @@ cm_yaw: softwareTrigger: false falcon: description: Falcon Sitoro detector - deviceClass: superxas_bec.devices.falcon_direct.FalconSuperXASDirect + deviceClass: superxas_bec.devices.falcon.FalconSuperXAS deviceConfig: prefix: 'X10DA-SITORO:' enabled: true onFailure: raise - readoutPriority: async + readoutPriority: monitored softwareTrigger: false filter_fe: description: Front End Filter diff --git a/superxas_bec/device_configs/x10da_config_falcon_test.yaml b/superxas_bec/device_configs/x10da_config_falcon_test.yaml index efacbb5..c785f88 100644 --- a/superxas_bec/device_configs/x10da_config_falcon_test.yaml +++ b/superxas_bec/device_configs/x10da_config_falcon_test.yaml @@ -1,6 +1,6 @@ falcon: description: Falcon Sitoro detector - deviceClass: superxas_bec.devices.falcon_slim.FalconSuperXASSlim + deviceClass: superxas_bec.devices.falcon.FalconSuperXAS deviceConfig: prefix: 'X10DA-SITORO:' enabled: true diff --git a/superxas_bec/device_configs/x10da_config_test.yaml b/superxas_bec/device_configs/x10da_config_test.yaml index 1d8d28f..4265c4e 100644 --- a/superxas_bec/device_configs/x10da_config_test.yaml +++ b/superxas_bec/device_configs/x10da_config_test.yaml @@ -104,14 +104,14 @@ trigger: ##### Falcon detector ##### falcon: - readoutPriority: monitored description: Falcon Sitoro detector deviceClass: superxas_bec.devices.falcon.FalconSuperXAS deviceConfig: prefix: 'X10DA-SITORO:' + enabled: true onFailure: raise - enabled: False - softwareTrigger: False + readoutPriority: monitored + softwareTrigger: false -- 2.49.1 From 377408ee05562444d9c50a54e17fd911bc4fbea7 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Fri, 27 Jun 2025 10:27:40 +0200 Subject: [PATCH 35/42] refactor(falcon): bugfix from beamlines --- superxas_bec/devices/falcon.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/superxas_bec/devices/falcon.py b/superxas_bec/devices/falcon.py index 1287a1a..d42770b 100644 --- a/superxas_bec/devices/falcon.py +++ b/superxas_bec/devices/falcon.py @@ -24,9 +24,8 @@ class FalconAcquiringStatus(int, enum.Enum): class DeadTimeCorrectedSignal(SignalRO): """Signal for dead time corrected counts.""" - def __init__(self, *args, parent: Device | None = None, **kwargs): - super().__init__(*args, **kwargs) - self.parent = parent + def __init__(self, name:str, *args, parent: Device | None = None, **kwargs): + super().__init__(name=name,*args, parent=parent,**kwargs) self._dead_time = 1.182e-7 # Dead time in seconds def get(self, **kwargs) -> float | None: @@ -112,7 +111,7 @@ class FalconControl(Device): # Configuration attributes collect_mode: EpicsSignal = Cpt(EpicsSignal, "CollectMode", doc="Collect mode signal") preset_real_time: EpicsSignal = Cpt( - EpicsSignal, "PresetRealTime", doc="Preset real time signal" + EpicsSignal, "PresetReal", doc="Preset real time signal" ) # Preview Signal for Falcon detector @@ -121,14 +120,12 @@ class FalconControl(Device): name="spectrum", ndim=1, kind=Kind.omitted, - auto_monitor=True, doc="Preview signal for Falcon detector spectrum", ) # Computed signal for dead time corrected counts dead_cor_roi0_count = Cpt( DeadTimeCorrectedSignal, - name="dead_cor_roi0_count", kind=Kind.hinted, doc="Dead time corrected ROI 0 count", ) -- 2.49.1 From 93e4ec30566ae4dea5df8f45e1a315b8dfc15bf1 Mon Sep 17 00:00:00 2001 From: appel_c Date: Mon, 30 Jun 2025 07:02:38 +0200 Subject: [PATCH 36/42] refactor(falcon): update test scripts --- bin/test_ad_based_falcon.py | 12 ++++++++---- bin/test_falcon.py | 7 +++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/bin/test_ad_based_falcon.py b/bin/test_ad_based_falcon.py index 73066a0..c7f9c4e 100644 --- a/bin/test_ad_based_falcon.py +++ b/bin/test_ad_based_falcon.py @@ -21,6 +21,13 @@ if __name__ == "__main__": falcon.stop_all.put(1) status.wait(timeout=5) + # Print PVs + print("PVs:") + print(f"ICR: {falcon.dxp1.input_count_rate.describe()}") + print(f"OCR: {falcon.dxp1.output_count_rate.describe()}") + print(f"ROI: {falcon.mca1.rois.count.describe()}") + print(f"ERT: {falcon.mca1.elapsed_real_time.describe()}") + # Test loop for i in range(500): start_time = time.time() @@ -43,8 +50,5 @@ if __name__ == "__main__": ocr = falcon.dxp1.output_count_rate.get() roi = falcon.mca1.rois.count.get() ert = falcon.mca1.elapsed_real_time.get() - dead_time_corrected_signal = falcon.dead_time_corrected_signal.get() - print( - f"time={time.time() - start_time:.4f}", icr, ocr, roi, ert, dead_time_corrected_signal - ) + print(f"time={time.time() - start_time:.4f}", icr, ocr, roi, ert) diff --git a/bin/test_falcon.py b/bin/test_falcon.py index 32806b0..5f4dd0d 100644 --- a/bin/test_falcon.py +++ b/bin/test_falcon.py @@ -21,6 +21,13 @@ if __name__ == "__main__": falcon.stop_all.put(1) status.wait(timeout=5) + # Print PVs + print("PVs:") + print(f"ICR: {falcon.icr.describe()}") + print(f"OCR: {falcon.ocr.describe()}") + print(f"ROI: {falcon.roi.describe()}") + print(f"ERT: {falcon.ert.describe()}") + # Test loop for i in range(500): start_time = time.time() -- 2.49.1 From 0cdd57e4d7571743f1049d3e29253052765ba8c8 Mon Sep 17 00:00:00 2001 From: appel_c Date: Mon, 30 Jun 2025 07:10:26 +0200 Subject: [PATCH 37/42] fix: deadtime corrected signal --- bin/test_falcon.py | 6 ++---- tests/tests_devices/test_devices_falcon.py | 14 ++++++-------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/bin/test_falcon.py b/bin/test_falcon.py index 5f4dd0d..6f0f461 100644 --- a/bin/test_falcon.py +++ b/bin/test_falcon.py @@ -50,8 +50,6 @@ if __name__ == "__main__": ocr = falcon.ocr.get() roi = falcon.roi.get() ert = falcon.ert.get() - dead_time_corrected_signal = falcon.dead_time_corrected_signal.get() + dead_cor_roi0_count = falcon.dead_cor_roi0_count.get() - print( - f"time={time.time() - start_time:.4f}", icr, ocr, roi, ert, dead_time_corrected_signal - ) + print(f"time={time.time() - start_time:.4f}", icr, ocr, roi, ert, dead_cor_roi0_count) diff --git a/tests/tests_devices/test_devices_falcon.py b/tests/tests_devices/test_devices_falcon.py index edea7f0..c6e7300 100644 --- a/tests/tests_devices/test_devices_falcon.py +++ b/tests/tests_devices/test_devices_falcon.py @@ -28,10 +28,10 @@ def falcon(): def test_devices_falcon(falcon): """Test init and on_connected methods of Falcon device""" - + falcon.acquiring._read_pv.mock_data = FalconAcquiringStatus.DONE assert falcon.prefix == "X10DA-SITORO:" assert falcon.name == "falcon" - assert falcon._pv_timeout == 1 + assert falcon._pv_timeout == 5 falcon.on_connected() @@ -41,17 +41,15 @@ def test_devices_falcon_stage(falcon): falcon.collect_mode.put(1) falcon.preset_real_time.put(1) falcon.stop_all.put(0) - falcon.acquiring.put(FalconAcquiringStatus.DONE) + falcon.acquiring._read_pv.mock_data = FalconAcquiringStatus.DONE # Should resolve with that status falcon.on_stage() assert falcon.collect_mode.get() == 0 assert falcon.preset_real_time.get() == 0 - assert falcon.stop_all.get() == 1 # Should timeout - falcon.acquiring.put(FalconAcquiringStatus.ACQUIRING) - falcon._pv_timeout = 0.1 - with pytest.raises(TimeoutError): - falcon.on_stage() + falcon.acquiring._read_pv.mock_data = FalconAcquiringStatus.ACQUIRING + falcon.on_stage() + assert falcon.stop_all.get() == 1 def test_devices_falcon_unstage(falcon): -- 2.49.1 From 2faf25db5284b0dcb30c73b63e7eb92cf548b157 Mon Sep 17 00:00:00 2001 From: appel_c Date: Mon, 30 Jun 2025 07:14:19 +0200 Subject: [PATCH 38/42] update script --- bin/test_ad_based_falcon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/test_ad_based_falcon.py b/bin/test_ad_based_falcon.py index c7f9c4e..cbe29f1 100644 --- a/bin/test_ad_based_falcon.py +++ b/bin/test_ad_based_falcon.py @@ -25,7 +25,7 @@ if __name__ == "__main__": print("PVs:") print(f"ICR: {falcon.dxp1.input_count_rate.describe()}") print(f"OCR: {falcon.dxp1.output_count_rate.describe()}") - print(f"ROI: {falcon.mca1.rois.count.describe()}") + print(f"ROI: {falcon.mca1.rois.roi0.count.describe()}") print(f"ERT: {falcon.mca1.elapsed_real_time.describe()}") # Test loop @@ -48,7 +48,7 @@ if __name__ == "__main__": icr = falcon.dxp1.input_count_rate.get() ocr = falcon.dxp1.output_count_rate.get() - roi = falcon.mca1.rois.count.get() + roi = falcon.mca1.rois.roi0.count.get() ert = falcon.mca1.elapsed_real_time.get() print(f"time={time.time() - start_time:.4f}", icr, ocr, roi, ert) -- 2.49.1 From 65e9be701785e97a91f2c283f8cd0e703dad8465 Mon Sep 17 00:00:00 2001 From: appel_c Date: Tue, 1 Jul 2025 08:48:02 +0200 Subject: [PATCH 39/42] wip add auto_monitor acquiring --- superxas_bec/devices/falcon_ad.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/superxas_bec/devices/falcon_ad.py b/superxas_bec/devices/falcon_ad.py index 5061dd3..22372db 100644 --- a/superxas_bec/devices/falcon_ad.py +++ b/superxas_bec/devices/falcon_ad.py @@ -5,7 +5,7 @@ import enum from bec_lib.devicemanager import ScanInfo from bec_lib.logger import bec_logger from ophyd import Component as Cpt -from ophyd import DeviceStatus, Staged, StatusBase +from ophyd import DeviceStatus, EpicsSignalRO, Staged, StatusBase from ophyd_devices import CompareStatus from ophyd_devices.devices.dxp import EpicsDXPFalcon, EpicsMCARecord, Falcon from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase @@ -23,6 +23,7 @@ class FalconAcquiringStatus(int, enum.Enum): class FalconADControl(Falcon): """Falcon Control class at SuperXAS. prefix: 'X10DA-SITORO:'""" + acquiring = Cpt(EpicsSignalRO, "Acquiring", auto_monitor=True, kind="omitted") # DXP parameters dxp1 = Cpt(EpicsDXPFalcon, "dxp1:") -- 2.49.1 From b9a4915797911b29fca6ca43c77326e5f7c3f37d Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Tue, 8 Jul 2025 10:10:13 +0200 Subject: [PATCH 40/42] fix ad_based falcon config --- bin/test_ad_based_falcon.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bin/test_ad_based_falcon.py b/bin/test_ad_based_falcon.py index cbe29f1..39b92ba 100644 --- a/bin/test_ad_based_falcon.py +++ b/bin/test_ad_based_falcon.py @@ -16,7 +16,7 @@ if __name__ == "__main__": trigger.wait_for_connection(timeout=50, all_signals=True) print(f"Device initialized in {time.time() - time_started:.2f} seconds.") - if falcon.acquiring.get != FalconAcquiringStatus.DONE: + if falcon.acquiring.get() != FalconAcquiringStatus.DONE: status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) falcon.stop_all.put(1) status.wait(timeout=5) @@ -28,12 +28,17 @@ if __name__ == "__main__": print(f"ROI: {falcon.mca1.rois.roi0.count.describe()}") print(f"ERT: {falcon.mca1.elapsed_real_time.describe()}") + # print(falcon.acquiring._auto_monitor) + # while True: + # print(falcon.acquiring.get()) + # time.sleep(1) # Test loop for i in range(500): start_time = time.time() + print(f"Start loop iteration {i}") status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING) - falcon.erase_start.put(1) + falcon.erase_start.put(1, use_complete=False) status.wait(timeout=5) status2 = CompareStatus(trigger.smpl_done, SamplingDone.DONE) -- 2.49.1 From 2bcb0ff9f11b8e997ee9706c512e242e0abcc518 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Tue, 8 Jul 2025 10:11:53 +0200 Subject: [PATCH 41/42] add config from 2507 --- .../device_configs/x10da_config_250707.yaml | 774 ++++++++++++++++++ 1 file changed, 774 insertions(+) create mode 100644 superxas_bec/device_configs/x10da_config_250707.yaml diff --git a/superxas_bec/device_configs/x10da_config_250707.yaml b/superxas_bec/device_configs/x10da_config_250707.yaml new file mode 100644 index 0000000..e7b7959 --- /dev/null +++ b/superxas_bec/device_configs/x10da_config_250707.yaml @@ -0,0 +1,774 @@ +absorption_transmission: + deviceClass: ophyd_devices.ComputedSignal + deviceConfig: + compute_method: "def compute_signals(signal1, signal2):\n import math\n return\ + \ math.log(signal1.get()/signal2.get())\n" + input_signals: + - ic1 + - ic2 + enabled: false + readOnly: false + readoutPriority: monitored +bm2_tr1: + description: Beam Monitor 2 Translation 1 + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-BM2:TR1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +bm2_tr2: + description: Beam Monitor 2 Translation 2 + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-BM2:TR2 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +bm3_tr1: + description: Beam Monitor 3 Translation 1 + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-BM3:TR1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +bm3_tr2: + description: Beam Monitor 3 Translation 2 + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-BM3:TR2 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_bnd: + description: Collimating Mirror bender + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:BND + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_pitch: + description: Collimating Mirror Pitch + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:ROTX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_roll: + description: Collimating Mirror Roll + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:ROTZ + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_trx: + description: Collimating Mirror X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:XTCP + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_trxd: + description: Collimating Mirror X-translation downstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:TRXD + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_trxu: + description: Collimating Mirror X-translation upstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:TRXU + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_try: + description: Collimating Mirror Point Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:YTCP + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_trydr: + description: Collimating Mirror Y-translation downstream ring + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:TRYDR + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_trydw: + description: Collimating Mirror Y-translation downstream wall + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:TRYDW + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_tryu: + description: Collimating Mirror Y-translation upstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:TRYU + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +cm_yaw: + description: Collimating Mirror Yaw + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-CM:ROTY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +falcon: + description: Falcon Sitoro detector + deviceClass: superxas_bec.devices.falcon.FalconSuperXAS + deviceConfig: + prefix: 'X10DA-SITORO:' + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +filter_fe: + description: Front End Filter + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-FI:TRY1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_bnd: + description: Focusing Mirror bender + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRB + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_pitch: + description: Focusing Mirror Pitch + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:pitch + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_roll: + description: Focusing Mirror Roll + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:roll + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_trx: + description: Focusing Mirror X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:trans + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_trxd: + description: Focusing Mirror X-translation downstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRXD + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_trxu: + description: Focusing Mirror X-translation upstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRXU + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_try: + description: Focusing Mirror Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:y + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_trydr: + description: Focusing Mirror Y-translation downstream ring + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRYDR + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_trydw: + description: Focusing Mirror Y-translation downstream + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRYDW + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_tryu: + description: Focusing Mirror Y-translation upstream wall + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:TRYU + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +fm_yaw: + description: Focusing Mirror Yaw + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-MI1:yaw + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +hrm_rotx: + description: Harmonic Rejection Mirror X-Rotation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-HRM:ROX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +hrm_try: + description: Harmonic Rejection Mirror Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-HRM:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +ic1: + description: Ionization Chamber 1 + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-ES1-SAI_01:MEAN + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +ic1_try: + description: Ionization Chamber 1 Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-IC1:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +ic2: + description: Ionization Chamber 2 + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-ES1-SAI_02:MEAN + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +ic2_try: + description: Ionization Chamber 2 Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-IC2:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +ic3: + description: Ionization Chamber 3 + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-ES1-SAI_03:MEAN + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +ic3_try: + description: Ionization Chamber 3 Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-IC3:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +ic4: + description: Ionization Chamber 4 + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-ES1-SAI_04:MEAN + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +manip_heavy_trx: + description: Heavy Sample Manipulator Motor X + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-PP1:MOT1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_heavy_try: + description: Heavy Sample Manipulator Motor Y + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-PP1:MOT2 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_heavy_trz: + description: Heavy Sample Manipulator Motor Z + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-PP1:MOT3 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_new_rot: + description: Old Sample Manipulator Base rotation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MAN:ROTY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_new_trx: + description: Old Sample Manipulator X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MAN:TRX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_new_try: + description: Old Sample Manipulator Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MAN:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_new_trz: + description: Old Sample Manipulator Z - Along beam + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MAN:TRZ + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_old_rot: + description: Old Sample Manipulator Base rotation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MA1:TRX2 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_old_trx: + description: Old Sample Manipulator X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MA1:TRX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_old_try: + description: Old Sample Manipulator Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MA1:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +manip_old_trz: + description: Old Sample Manipulator Z - Along beam + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-MA1:TRX1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +mono_encoder_deg: + description: ROTX encoder of Mono converted to degrees + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-OP1-MO1:ENC-ROTXCalc.VAL + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +mono_encoder_raw: + description: ROTX encoder of Mono raw value + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-OP1-MO1:ENC-ROTX.RVAL + enabled: true + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +mono_energy: + description: Mono Energy + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-MO12-QEXAFS:E_TEST + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +mono_rotx: + description: Monochromator 1 X-Rotation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP1-MO1:ROTX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +mono_trx: + description: Monochromator 1 X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP1-MO1:TRX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +mono_try: + description: Monochromator 1 Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP1-MO1:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_centerx: + description: Front-end slit diaphragm X-center + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:CENTERX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_centery: + description: Front-end slit diaphragm Y-center + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:CENTERY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_gapx: + description: Front-end slit diaphragm X-gap + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:GAPX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_gapy: + description: Front-end slit diaphragm Y-gap + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:GAPY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_trxr: + description: Front-end slit diaphragm 1 X-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:TRXR + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_trxw: + description: Front-end slit diaphragm 2 X-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:TRXW + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_tryb: + description: Front-end slit diaphragm 1 Y-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:TRYB + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_fe_tryt: + description: Front-end slit diaphragm 2 Y-translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-FE-SLDI:TRYT + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_kb_centerx: + description: KB slit axis X + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-SH1:POSX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_kb_centery: + description: KB slit axis Y + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-SV1:POSY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_kb_gapx: + description: KB slit axis X + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-SH1:OPENX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_kb_gapy: + description: KB slit axis Y + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-SV1:OPENY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_centerx: + description: Optics slit X-center + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SHtestCENTER + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_centery: + description: Optics slit Y-center + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SVcenter + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_gapx: + description: Optics slit X-gap + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SHsize + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_gapy: + description: Optics slit Y-gap + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SVsize + enabled: false + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_htr1: + description: Optics slit X-translation Ring + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SH1:TR1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_htr2: + description: Optics slit X-translation Wall + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SH1:TR2 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_vtr1: + description: Optics slit Y-translation Up + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SV1:TR1 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +slit_op_vtr2: + description: Optics slit Y-translation Down + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-OP-SV1:TR2 + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +t_absorber1: + description: AbsorberTemperature 1 + deviceClass: ophyd.EpicsSignalRO + deviceConfig: + auto_monitor: true + read_pv: X10DA-FE-ABS1-ETTC-0010:TEMP + enabled: false + onFailure: raise + readoutPriority: monitored + softwareTrigger: false +table1_trx: + description: Experimental Table 1 X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-ET1:TRX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +table1_try: + description: Experimental Table 1 Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-ET1:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +table2_trx: + description: Experimental Table 2 X-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES2-ET2:TRX + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +table2_try: + description: Experimental Table 2 Y-Translation + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES2-ET2:TRY + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +trigger: + description: Trigger Card + deviceClass: superxas_bec.devices.trigger.Trigger + deviceConfig: + prefix: 'X10DA-ES1:' + enabled: true + onFailure: raise + readoutPriority: baseline + softwareTrigger: true +xrayeye_foc: + description: X-Ray Eye Fine Focus + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-XE1:FINFOC + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +xrayeye_zoom: + description: X-Ray Eye Zoom + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES1-XE1:ZOOM + enabled: true + onFailure: retry + readoutPriority: baseline + softwareTrigger: false +lasermir_rot: + description: Laser mirror + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES4-CRMO4:MOT2 + enabled: true + onFailure: retry + readoutPriority: monitored + softwareTrigger: false +lasermir_sw: + description: Laser mirror + deviceClass: ophyd.EpicsMotor + deviceConfig: + prefix: X10DA-ES4-CRMO4:MOT3 + enabled: true + onFailure: retry + readoutPriority: monitored + softwareTrigger: false \ No newline at end of file -- 2.49.1 From 72c044bc11ec815b54e1dcea3ccb8e699b2bf640 Mon Sep 17 00:00:00 2001 From: gac-x10da Date: Tue, 8 Jul 2025 10:12:37 +0200 Subject: [PATCH 42/42] add updated falcon test scripts --- bin/test_ad_based_falcon.py | 6 +++--- bin/test_falcon.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/test_ad_based_falcon.py b/bin/test_ad_based_falcon.py index 39b92ba..d5f2ffd 100644 --- a/bin/test_ad_based_falcon.py +++ b/bin/test_ad_based_falcon.py @@ -37,9 +37,9 @@ if __name__ == "__main__": start_time = time.time() print(f"Start loop iteration {i}") - status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING) - falcon.erase_start.put(1, use_complete=False) - status.wait(timeout=5) + #status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.ACQUIRING) + falcon.erase_start.put(1) + #status.wait(timeout=5) status2 = CompareStatus(trigger.smpl_done, SamplingDone.DONE) trigger.smpl.put(1) diff --git a/bin/test_falcon.py b/bin/test_falcon.py index 6f0f461..b314f14 100644 --- a/bin/test_falcon.py +++ b/bin/test_falcon.py @@ -16,7 +16,7 @@ if __name__ == "__main__": trigger.wait_for_connection(timeout=50, all_signals=True) print(f"Device initialized in {time.time() - time_started:.2f} seconds.") - if falcon.acquiring.get != FalconAcquiringStatus.DONE: + if falcon.acquiring.get() != FalconAcquiringStatus.DONE: status = CompareStatus(falcon.acquiring, FalconAcquiringStatus.DONE) falcon.stop_all.put(1) status.wait(timeout=5) -- 2.49.1