From ff0442e6010c3744eab47aaa556c9d3d5ea5388a Mon Sep 17 00:00:00 2001 From: appel_c Date: Tue, 21 Apr 2026 18:00:33 +0200 Subject: [PATCH 1/3] feat: add simulated devices for endstation, fsh and ddg1 --- .../device_configs/simulated_omny/__init__.py | 0 .../simulated_bl_endstation.yaml | 18 +++++++ csaxs_bec/devices/sim/__init__.py | 0 .../devices/sim/simulated_beamline_devices.py | 50 +++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 csaxs_bec/device_configs/simulated_omny/__init__.py create mode 100644 csaxs_bec/device_configs/simulated_omny/simulated_bl_endstation.yaml create mode 100644 csaxs_bec/devices/sim/__init__.py create mode 100644 csaxs_bec/devices/sim/simulated_beamline_devices.py diff --git a/csaxs_bec/device_configs/simulated_omny/__init__.py b/csaxs_bec/device_configs/simulated_omny/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/csaxs_bec/device_configs/simulated_omny/simulated_bl_endstation.yaml b/csaxs_bec/device_configs/simulated_omny/simulated_bl_endstation.yaml new file mode 100644 index 0000000..4c77216 --- /dev/null +++ b/csaxs_bec/device_configs/simulated_omny/simulated_bl_endstation.yaml @@ -0,0 +1,18 @@ +ddg1: + description: Main delay Generator for triggering + deviceClass: csaxs_bec.devices.sim.simulated_beamline_devices.SimulatedDDG1 + enabled: true + deviceConfig: + prefix: 'X12SA-CPCL-DDG1:' + onFailure: raise + readOnly: false + readoutPriority: baseline + softwareTrigger: true +fsh: + description: Fast shutter manual control and readback + deviceClass: csaxs_bec.devices.sim.simulated_beamline_devices.cSAXSSimulatedFastShutter + deviceConfig: + prefix: 'X12SA-ES1-TTL:' + onFailure: raise + enabled: true + readoutPriority: monitored \ No newline at end of file diff --git a/csaxs_bec/devices/sim/__init__.py b/csaxs_bec/devices/sim/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/csaxs_bec/devices/sim/simulated_beamline_devices.py b/csaxs_bec/devices/sim/simulated_beamline_devices.py new file mode 100644 index 0000000..0e5e479 --- /dev/null +++ b/csaxs_bec/devices/sim/simulated_beamline_devices.py @@ -0,0 +1,50 @@ +"""This module contains simulated versions of beamline devices relevant for running simulated experiments.""" + +from __future__ import annotations + +from ophyd import Device +from ophyd_devices import StatusBase +from ophyd_devices.tests.utils import patched_device + +from csaxs_bec.devices.epics.delay_generator_csaxs.ddg_1 import DDG1 +from csaxs_bec.devices.epics.fast_shutter import cSAXSFastEpicsShutter + + +class cSAXSSimulatedFastShutter(Device): + """ + Simulated version of the cSAXS Fast Shutter. + + To set up initial signal to specific values, please use the example below: + fsh.shutter._read_pv.mock_data = 1 # + -> with 'shutter' being a signal of the shutter device + + """ + + def __new__(cls, *args, **kwargs): + with patched_device(cSAXSFastEpicsShutter, *args, **kwargs) as fsh: + # fsh.shutter._read_pv.mock_data = 1 # Set mock data if needed + return fsh + + +class DDG1WithPatchedStage(DDG1): + """ + Simulated version of the DDG1 with patched stage method. + We inherit here to be able to overwrite the on_stage method if needed, without compromising the rest of the device's functionality. + """ + + def on_trigger(self): + """ + We overwrite and patch the on_trigger method. This resolves immediately now and + sets the status to finished, instead of waiting for the trigger logic to complete. + """ + # Change logic if needed + status = StatusBase(obj=self) + status.set_finished() + return status + + +class SimulatedDDG1(Device): + + def __new__(cls, *args, **kwargs): + with patched_device(DDG1, *args, **kwargs) as ddg1: + return ddg1 -- 2.52.0 From 0bb6c6ce54c94ec9c6c762d1c3be65da61d09b01 Mon Sep 17 00:00:00 2001 From: x12sa Date: Wed, 22 Apr 2026 12:34:26 +0200 Subject: [PATCH 2/3] replaced omnyfsh by fsh --- .../plugins/LamNI/lamni_optics_mixin.py | 2 +- .../plugins/flomni/flomni_optics_mixin.py | 2 +- .../plugins/flomni/x_ray_eye_align.py | 6 +++--- .../plugins/omny/omny_optics_mixin.py | 2 +- .../bec_widgets/widgets/xray_eye/x_ray_eye.py | 12 ++++++------ csaxs_bec/device_configs/ptycho_flomni.yaml | 17 +++++++++-------- .../simulated_omny/simulated_bl_endstation.yaml | 9 ++++++--- 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/LamNI/lamni_optics_mixin.py b/csaxs_bec/bec_ipython_client/plugins/LamNI/lamni_optics_mixin.py index 2437bf9..f40fd88 100644 --- a/csaxs_bec/bec_ipython_client/plugins/LamNI/lamni_optics_mixin.py +++ b/csaxs_bec/bec_ipython_client/plugins/LamNI/lamni_optics_mixin.py @@ -172,7 +172,7 @@ class LamNIOpticsMixin: def leye_out(self): self.loptics_in() - dev.omnyfsh.fshopen() + dev.fsh.fshopen() leyey_out = self._get_user_param_safe("leyey", "out") umv(dev.leyey, leyey_out) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py index 4e9e688..510294b 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py @@ -16,7 +16,7 @@ class FlomniOpticsMixin: return param.get(var) def feye_out(self): - dev.omnyfsh.fshclose() + dev.fsh.fshclose() self.foptics_in() self.flomnigui_show_xeyealign() self.xrayeye_update_frame() diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py b/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py index e1fc704..70dea21 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py @@ -57,14 +57,14 @@ class XrayEyeAlign: self.gui.on_live_view_enabled(True) - dev.omnyfsh.fshopen() + dev.fsh.fshopen() time.sleep(0.5) # stop live view if not keep_shutter_open: self.gui.on_live_view_enabled(False) time.sleep(0.1) - dev.omnyfsh.fshclose() + dev.fsh.fshclose() print("Received new frame.") else: print("Staying in live view, shutter is and remains open!") @@ -233,7 +233,7 @@ class XrayEyeAlign: if keep_shutter_open: if self.flomni.OMNYTools.yesno("Close the shutter now?", "y"): - dev.omnyfsh.fshclose() + dev.fsh.fshclose() self.gui.on_live_view_enabled(False) print("setting 'XOMNYI-XEYE-ACQ:0'") diff --git a/csaxs_bec/bec_ipython_client/plugins/omny/omny_optics_mixin.py b/csaxs_bec/bec_ipython_client/plugins/omny/omny_optics_mixin.py index e9ff998..d30a224 100644 --- a/csaxs_bec/bec_ipython_client/plugins/omny/omny_optics_mixin.py +++ b/csaxs_bec/bec_ipython_client/plugins/omny/omny_optics_mixin.py @@ -48,7 +48,7 @@ class OMNYOpticsMixin: dev.oeyez.controller.socket_put_confirmed("axspeed[7]=10000") def oeye_out(self): - dev.omnyfsh.fshclose() + dev.fsh.fshclose() if self.OMNYTools.yesno("Did you move in the optics?"): umv(dev.oeyez, -2) self._oeyey_mv(-60.3) diff --git a/csaxs_bec/bec_widgets/widgets/xray_eye/x_ray_eye.py b/csaxs_bec/bec_widgets/widgets/xray_eye/x_ray_eye.py index cde9f88..f5fd0af 100644 --- a/csaxs_bec/bec_widgets/widgets/xray_eye/x_ray_eye.py +++ b/csaxs_bec/bec_widgets/widgets/xray_eye/x_ray_eye.py @@ -154,7 +154,7 @@ class XRayEye(BECWidget, QWidget): # Connection to redis endpoints self.bec_dispatcher.connect_slot( - self.getting_shutter_status, MessageEndpoints.device_readback("omnyfsh") + self.getting_shutter_status, MessageEndpoints.device_readback("fsh") ) self.bec_dispatcher.connect_slot( self.getting_camera_status, MessageEndpoints.device_read_configuration(CAMERA[0]) @@ -342,7 +342,7 @@ class XRayEye(BECWidget, QWidget): def _init_gui_trigger(self): self.dev.omny_xray_gui.read() - self.dev.omnyfsh.read() + self.dev.fsh.read() ################################################################################ # Device Connection logic @@ -451,15 +451,15 @@ class XRayEye(BECWidget, QWidget): logger.info(f"Shutter changed from GUI to: {enabled}") self.shutter_toggle.blockSignals(True) if enabled: - self.dev.omnyfsh.fshopen() + self.dev.fsh.fshopen() else: - self.dev.omnyfsh.fshclose() + self.dev.fsh.fshclose() # self.shutter_toggle.checked = enabled self.shutter_toggle.blockSignals(False) @SafeSlot(dict, dict) def getting_shutter_status(self, data, meta): - shutter_open = bool(data.get("signals").get("omnyfsh_shutter").get("value")) + shutter_open = bool(data.get("signals").get("fsh_shutter").get("value")) self.shutter_toggle.blockSignals(True) self.shutter_toggle.checked = shutter_open self.shutter_toggle.blockSignals(False) @@ -567,7 +567,7 @@ class XRayEye(BECWidget, QWidget): ) self.bec_dispatcher.disconnect_slot( - self.getting_shutter_status, MessageEndpoints.device_readback("omnyfsh") + self.getting_shutter_status, MessageEndpoints.device_readback("fsh") ) self.bec_dispatcher.disconnect_slot( self.getting_camera_status, MessageEndpoints.device_read_configuration(CAMERA[0]) diff --git a/csaxs_bec/device_configs/ptycho_flomni.yaml b/csaxs_bec/device_configs/ptycho_flomni.yaml index dbfd9ff..78296a1 100644 --- a/csaxs_bec/device_configs/ptycho_flomni.yaml +++ b/csaxs_bec/device_configs/ptycho_flomni.yaml @@ -477,15 +477,16 @@ flomni_temphum: readoutPriority: baseline # ############################################################ # ########## OMNY / flOMNI / LamNI fast shutter ############## +# REPLACED BY SIMULATED BEAMLINE DEVICE !!!!!!!!!!!!!!!! # ############################################################ -omnyfsh: - description: omnyfsh connects to fast shutter at X12 if device fsh exists - deviceClass: csaxs_bec.devices.omny.shutter.OMNYFastShutter - deviceConfig: {} - enabled: true - onFailure: buffer - readOnly: false - readoutPriority: baseline +# omnyfsh: +# description: omnyfsh connects to fast shutter at X12 if device fsh exists +# deviceClass: csaxs_bec.devices.omny.shutter.OMNYFastShutter +# deviceConfig: {} +# enabled: true +# onFailure: buffer +# readOnly: false +# readoutPriority: baseline ############################################################ #################### GUI Signals ########################### ############################################################ diff --git a/csaxs_bec/device_configs/simulated_omny/simulated_bl_endstation.yaml b/csaxs_bec/device_configs/simulated_omny/simulated_bl_endstation.yaml index 4c77216..2ac5201 100644 --- a/csaxs_bec/device_configs/simulated_omny/simulated_bl_endstation.yaml +++ b/csaxs_bec/device_configs/simulated_omny/simulated_bl_endstation.yaml @@ -1,5 +1,5 @@ ddg1: - description: Main delay Generator for triggering + description: Simulated main delay Generator for triggering deviceClass: csaxs_bec.devices.sim.simulated_beamline_devices.SimulatedDDG1 enabled: true deviceConfig: @@ -9,10 +9,13 @@ ddg1: readoutPriority: baseline softwareTrigger: true fsh: - description: Fast shutter manual control and readback + description: Simulated fast shutter manual control and readback deviceClass: csaxs_bec.devices.sim.simulated_beamline_devices.cSAXSSimulatedFastShutter deviceConfig: prefix: 'X12SA-ES1-TTL:' onFailure: raise enabled: true - readoutPriority: monitored \ No newline at end of file + readoutPriority: monitored + +flomni: + - !include ../ptycho_flomni.yaml \ No newline at end of file -- 2.52.0 From 8fca6bed113e0ea62190310d05e2e34c1e81b62e Mon Sep 17 00:00:00 2001 From: appel_c Date: Wed, 22 Apr 2026 17:17:35 +0200 Subject: [PATCH 3/3] fix: Add logs for simulated DDG --- csaxs_bec/devices/sim/simulated_beamline_devices.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/csaxs_bec/devices/sim/simulated_beamline_devices.py b/csaxs_bec/devices/sim/simulated_beamline_devices.py index 0e5e479..d359d6f 100644 --- a/csaxs_bec/devices/sim/simulated_beamline_devices.py +++ b/csaxs_bec/devices/sim/simulated_beamline_devices.py @@ -2,6 +2,7 @@ from __future__ import annotations +from bec_lib.logger import bec_logger from ophyd import Device from ophyd_devices import StatusBase from ophyd_devices.tests.utils import patched_device @@ -9,6 +10,8 @@ from ophyd_devices.tests.utils import patched_device from csaxs_bec.devices.epics.delay_generator_csaxs.ddg_1 import DDG1 from csaxs_bec.devices.epics.fast_shutter import cSAXSFastEpicsShutter +logger = bec_logger.logger + class cSAXSSimulatedFastShutter(Device): """ @@ -32,6 +35,12 @@ class DDG1WithPatchedStage(DDG1): We inherit here to be able to overwrite the on_stage method if needed, without compromising the rest of the device's functionality. """ + def on_stage(self): + logger.warning( + f"Staging of {self.name}. This is a simulated device of {self.__class__.__name__}." + ) + super().on_stage() + def on_trigger(self): """ We overwrite and patch the on_trigger method. This resolves immediately now and @@ -46,5 +55,5 @@ class DDG1WithPatchedStage(DDG1): class SimulatedDDG1(Device): def __new__(cls, *args, **kwargs): - with patched_device(DDG1, *args, **kwargs) as ddg1: + with patched_device(DDG1WithPatchedStage, *args, **kwargs) as ddg1: return ddg1 -- 2.52.0