From 2835ca52f97be41992515557022bfc2132cffd91 Mon Sep 17 00:00:00 2001 From: x12sa Date: Thu, 9 Apr 2026 12:17:31 +0200 Subject: [PATCH 1/5] csaxs bpm show all --- .../plugins/cSAXS/diagnostics.py | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 csaxs_bec/bec_ipython_client/plugins/cSAXS/diagnostics.py diff --git a/csaxs_bec/bec_ipython_client/plugins/cSAXS/diagnostics.py b/csaxs_bec/bec_ipython_client/plugins/cSAXS/diagnostics.py new file mode 100644 index 0000000..14a8cec --- /dev/null +++ b/csaxs_bec/bec_ipython_client/plugins/cSAXS/diagnostics.py @@ -0,0 +1,132 @@ +from __future__ import annotations +import builtins + +if builtins.__dict__.get("dev") is not None: + dev = builtins.__dict__.get("dev") + + +# ============================================================ +# Base classes +# ============================================================ + +class SingleSignalDiag: + """Diagnostics for a single-signal device with gain control.""" + + def __init__(self, name, label, gain_dev, signal_dev): + self._name = name + self._label = label + self._gain = gain_dev + self._signal = signal_dev + + def show_all(self): + cfg = self._gain.get() + try: + val = self._signal.signal.get() + sval = f"{val:.4f}" + except Exception: + sval = "N/A" + + print(self._label) + print("-" * len(self._label)) + print(f"gain : {cfg.gain:.0e}") + print(f"coupling : {cfg.coupling}") + print(f"speed : {cfg.speed}") + print(f"readback : {sval}") + print("") + print("hint:") + print(f" gain : csaxs.diagnostics.{self._name}.gain(1e5)") + print(f" raw : dev.{self._signal.name}.signal.get()") + + def gain(self, value): + self._gain.set_gain(value) + + +class BPMDiag: + """Diagnostics for a BPM device.""" + + def __init__(self, name, label, gain_dev, rb_dev): + self._name = name + self._label = label + self._gain = gain_dev + self._rb = rb_dev + + def show_all(self): + cfg = self._gain.get() + rb = self._rb.get() + + print(self._label) + print("-" * len(self._label)) + print(f"gain : {cfg.gain:.0e}") + print(f"coupling : {cfg.coupling}") + print(f"speed : {cfg.speed}") + print("readback:") + print(f" pos_x : {rb.pos_x:.3e}") + print(f" pos_y : {rb.pos_y:.3e}") + print(f" intensity : {rb.intensity:.3f}") + print("") + print("hint:") + print(f" gain : csaxs.diagnostics.{self._name}.gain(1e6)") + print(f" raw : dev.{self._rb.name}.pos_x.get()") + + def gain(self, value): + self._gain.set_gain(value) + + +# ============================================================ +# Diagnostics root (user-facing object) +# ============================================================ + +class cSAXSDiagnostics: + """User-visible diagnostics namespace.""" + + def __init__(self): + # BPMs + self.bpm_xbox1 = BPMDiag( + name="bpm_xbox1", + label="BPM XBox1 (OP hutch)", + gain_dev=dev.gain_bpm_xbox1, + rb_dev=dev.bpm_xbox1_slowrb, + ) + + self.bpm_xbox2 = BPMDiag( + name="bpm_xbox2", + label="BPM XBox2 (ES hutch)", + gain_dev=dev.gain_bpm_xbox2, + rb_dev=dev.bpm_xbox2_slowrb, + ) + + # Single-signal diagnostics + self.bim = SingleSignalDiag( + name="bim", + label="BIM XBox3 (ES)", + gain_dev=dev.gain_bim_xbox3, + signal_dev=dev.bim_xbox3_slowrb, + ) + + self.beamstop = SingleSignalDiag( + name="beamstop", + label="Beamstop diode (flight tube)", + gain_dev=dev.gain_beamstop_diode, + signal_dev=dev.beamstop_intensity, + ) + + self.polarization = SingleSignalDiag( + name="polarization", + label="Polarization diodes (XBox1)", + gain_dev=dev.gain_diodes_xbox1, + signal_dev=dev.diode_horizontal_xbox1_slowrb, + ) + + def show_all(self): + print("CSAXS diagnostics") + print("=================") + print("") + self.bpm_xbox1.show_all() + print("") + self.bpm_xbox2.show_all() + print("") + self.bim.show_all() + print("") + self.beamstop.show_all() + print("") + self.polarization.show_all() -- 2.52.0 From f45382a3d9d7c5ad621bd3e5b35529fef1f0fe36 Mon Sep 17 00:00:00 2001 From: x12sa Date: Thu, 9 Apr 2026 12:17:42 +0200 Subject: [PATCH 2/5] removed galilrio for now --- csaxs_bec/device_configs/bl_endstation.yaml | 206 +++++++++--------- csaxs_bec/device_configs/bl_optics_hutch.yaml | 158 +++++++------- 2 files changed, 182 insertions(+), 182 deletions(-) diff --git a/csaxs_bec/device_configs/bl_endstation.yaml b/csaxs_bec/device_configs/bl_endstation.yaml index b8fba6b..c543a76 100644 --- a/csaxs_bec/device_configs/bl_endstation.yaml +++ b/csaxs_bec/device_configs/bl_endstation.yaml @@ -888,120 +888,120 @@ dettrx: -#################### -### BPM XBox2 (first Xbox in ES) and Xbox3 BIM -### This requires galilrioesxbox device. On top of that the gain control devices and slow readback devices are built. -#################### +# #################### +# ### BPM XBox2 (first Xbox in ES) and Xbox3 BIM +# ### This requires galilrioesxbox device. On top of that the gain control devices and slow readback devices are built. +# #################### -galilrioesxbox: - description: Galil RIO for remote gain switching and slow reading XBox ES - deviceClass: csaxs_bec.devices.omny.galil.galil_rio.GalilRIO - deviceConfig: - host: galilrioesxbox.psi.ch - enabled: true - onFailure: retry - readOnly: false - readoutPriority: baseline - connectionTimeout: 20 +# galilrioesxbox: +# description: Galil RIO for remote gain switching and slow reading XBox ES +# deviceClass: csaxs_bec.devices.omny.galil.galil_rio.GalilRIO +# deviceConfig: +# host: galilrioesxbox.psi.ch +# enabled: true +# onFailure: retry +# readOnly: false +# readoutPriority: baseline +# connectionTimeout: 20 -gain_bpm_xbox2: - description: Gain control for BPM XBox2 - deviceClass: csaxs_bec.devices.pseudo_devices.bpm_control.BPMControl - deviceConfig: - gain_lsb: galilrioesxbox.digital_out.ch0 # Pin 10 -> Galil ch0 - gain_mid: galilrioesxbox.digital_out.ch1 # Pin 11 -> Galil ch1 - gain_msb: galilrioesxbox.digital_out.ch2 # Pin 12 -> Galil ch2 - coupling: galilrioesxbox.digital_out.ch3 # Pin 13 -> Galil ch3 - speed_mode: galilrioesxbox.digital_out.ch4 # Pin 14 -> Galil ch4 - enabled: true - readoutPriority: baseline - onFailure: retry - needs: - - galilrioesxbox +# gain_bpm_xbox2: +# description: Gain control for BPM XBox2 +# deviceClass: csaxs_bec.devices.pseudo_devices.bpm_control.BPMControl +# deviceConfig: +# gain_lsb: galilrioesxbox.digital_out.ch0 # Pin 10 -> Galil ch0 +# gain_mid: galilrioesxbox.digital_out.ch1 # Pin 11 -> Galil ch1 +# gain_msb: galilrioesxbox.digital_out.ch2 # Pin 12 -> Galil ch2 +# coupling: galilrioesxbox.digital_out.ch3 # Pin 13 -> Galil ch3 +# speed_mode: galilrioesxbox.digital_out.ch4 # Pin 14 -> Galil ch4 +# enabled: true +# readoutPriority: baseline +# onFailure: retry +# needs: +# - galilrioesxbox -bpm_xbox2_slowrb: - description: BPM Xbox 2 (First Xbox in ES hutch) readback - deviceClass: csaxs_bec.devices.pseudo_devices.bpm.BPM - deviceConfig: - left_top: galilrioesxbox.analog_in.ch0 - right_top: galilrioesxbox.analog_in.ch1 - right_bot: galilrioesxbox.analog_in.ch2 - left_bot: galilrioesxbox.analog_in.ch3 - enabled: true - readoutPriority: baseline - onFailure: retry - needs: - - galilrioesxbox +# bpm_xbox2_slowrb: +# description: BPM Xbox 2 (First Xbox in ES hutch) readback +# deviceClass: csaxs_bec.devices.pseudo_devices.bpm.BPM +# deviceConfig: +# left_top: galilrioesxbox.analog_in.ch0 +# right_top: galilrioesxbox.analog_in.ch1 +# right_bot: galilrioesxbox.analog_in.ch2 +# left_bot: galilrioesxbox.analog_in.ch3 +# enabled: true +# readoutPriority: baseline +# onFailure: retry +# needs: +# - galilrioesxbox -gain_bim_xbox3: - description: Gain control for beam intensity monitor ES Xbox3 - deviceClass: csaxs_bec.devices.pseudo_devices.bpm_control.BPMControl - deviceConfig: - gain_lsb: galilrioesxbox.digital_out.ch6 # Pin 10 -> Galil ch0 - gain_mid: galilrioesxbox.digital_out.ch7 # Pin 11 -> Galil ch1 - gain_msb: galilrioesxbox.digital_out.ch8 # Pin 12 -> Galil ch2 - coupling: galilrioesxbox.digital_out.ch9 # Pin 13 -> Galil ch3 - speed_mode: galilrioesxbox.digital_out.ch10 # Pin 14 -> Galil ch4 - enabled: true - readoutPriority: baseline - onFailure: retry - needs: - - galilrioesxbox +# gain_bim_xbox3: +# description: Gain control for beam intensity monitor ES Xbox3 +# deviceClass: csaxs_bec.devices.pseudo_devices.bpm_control.BPMControl +# deviceConfig: +# gain_lsb: galilrioesxbox.digital_out.ch6 # Pin 10 -> Galil ch0 +# gain_mid: galilrioesxbox.digital_out.ch7 # Pin 11 -> Galil ch1 +# gain_msb: galilrioesxbox.digital_out.ch8 # Pin 12 -> Galil ch2 +# coupling: galilrioesxbox.digital_out.ch9 # Pin 13 -> Galil ch3 +# speed_mode: galilrioesxbox.digital_out.ch10 # Pin 14 -> Galil ch4 +# enabled: true +# readoutPriority: baseline +# onFailure: retry +# needs: +# - galilrioesxbox -bim_xbox3_slowrb: - description: Beam intensity slow readback ES XBox3 - deviceClass: csaxs_bec.devices.pseudo_devices.signal_forwarder.SignalForwarder - deviceConfig: - signal: galilrioesxbox.analog_in.ch6 - enabled: true - readoutPriority: baseline - onFailure: retry - needs: - - galilrioesxbox +# bim_xbox3_slowrb: +# description: Beam intensity slow readback ES XBox3 +# deviceClass: csaxs_bec.devices.pseudo_devices.signal_forwarder.SignalForwarder +# deviceConfig: +# signal: galilrioesxbox.analog_in.ch6 +# enabled: true +# readoutPriority: baseline +# onFailure: retry +# needs: +# - galilrioesxbox -#################### -### Beamstop diode control for flight tube -### This requires galilrioft device. On top of that the gain control device is built as well as a slow voltage readback. -#################### +# #################### +# ### Beamstop diode control for flight tube +# ### This requires galilrioft device. On top of that the gain control device is built as well as a slow voltage readback. +# #################### -galilrioesft: - description: Galil RIO for remote gain switching and slow reading FlightTube - deviceClass: csaxs_bec.devices.omny.galil.galil_rio.GalilRIO - deviceConfig: - host: galilrioesft.psi.ch - enabled: true - onFailure: retry - readOnly: false - readoutPriority: baseline - connectionTimeout: 20 +# galilrioesft: +# description: Galil RIO for remote gain switching and slow reading FlightTube +# deviceClass: csaxs_bec.devices.omny.galil.galil_rio.GalilRIO +# deviceConfig: +# host: galilrioesft.psi.ch +# enabled: true +# onFailure: retry +# readOnly: false +# readoutPriority: baseline +# connectionTimeout: 20 -gain_beamstop_diode: - description: Gain control for beamstop flightube - deviceClass: csaxs_bec.devices.pseudo_devices.bpm_control.BPMControl - deviceConfig: - gain_lsb: galilrioesft.digital_out.ch0 # Pin 10 -> Galil ch0 - gain_mid: galilrioesft.digital_out.ch1 # Pin 11 -> Galil ch1 - gain_msb: galilrioesft.digital_out.ch2 # Pin 12 -> Galil ch2 - coupling: galilrioesft.digital_out.ch3 # Pin 13 -> Galil ch3 - speed_mode: galilrioesft.digital_out.ch4 # Pin 14 -> Galil ch4 - enabled: true - readoutPriority: baseline - onFailure: retry - needs: - - galilrioesft +# gain_beamstop_diode: +# description: Gain control for beamstop flightube +# deviceClass: csaxs_bec.devices.pseudo_devices.bpm_control.BPMControl +# deviceConfig: +# gain_lsb: galilrioesft.digital_out.ch0 # Pin 10 -> Galil ch0 +# gain_mid: galilrioesft.digital_out.ch1 # Pin 11 -> Galil ch1 +# gain_msb: galilrioesft.digital_out.ch2 # Pin 12 -> Galil ch2 +# coupling: galilrioesft.digital_out.ch3 # Pin 13 -> Galil ch3 +# speed_mode: galilrioesft.digital_out.ch4 # Pin 14 -> Galil ch4 +# enabled: true +# readoutPriority: baseline +# onFailure: retry +# needs: +# - galilrioesft -beamstop_intensity: - description: Beamstop intensity from Galil analog input ch6 - deviceClass: csaxs_bec.devices.pseudo_devices.signal_forwarder.SignalForwarder - deviceConfig: - signal: galilrioesft.analog_in.ch0 - enabled: true - readoutPriority: baseline - onFailure: retry - needs: - - galilrioesft +# beamstop_intensity: +# description: Beamstop intensity from Galil analog input ch6 +# deviceClass: csaxs_bec.devices.pseudo_devices.signal_forwarder.SignalForwarder +# deviceConfig: +# signal: galilrioesft.analog_in.ch0 +# enabled: true +# readoutPriority: baseline +# onFailure: retry +# needs: +# - galilrioesft diff --git a/csaxs_bec/device_configs/bl_optics_hutch.yaml b/csaxs_bec/device_configs/bl_optics_hutch.yaml index 3486eaf..651b2cc 100644 --- a/csaxs_bec/device_configs/bl_optics_hutch.yaml +++ b/csaxs_bec/device_configs/bl_optics_hutch.yaml @@ -197,89 +197,89 @@ ccm_energy: -#################### -### BPM and polarization diag XBox1 (optics hutch) -### This requires galilrioop device. On top of that the gain control devices and slow readback devices are built. -### dev.galilrioop.read() shows the analog inputs -### another example ...analog_in.ch0.get() -### dev.galilrioop.read_configuration() shows the digital channels -### example for direct access dev.galilrioesxbox.digital_out.ch1.put(0) -#################### +# #################### +# ### BPM and polarization diag XBox1 (optics hutch) +# ### This requires galilrioop device. On top of that the gain control devices and slow readback devices are built. +# ### dev.galilrioop.read() shows the analog inputs +# ### another example ...analog_in.ch0.get() +# ### dev.galilrioop.read_configuration() shows the digital channels +# ### example for direct access dev.galilrioesxbox.digital_out.ch1.put(0) +# #################### -galilrioop: - description: Galil RIO for remote gain switching and slow reading XBox OP - deviceClass: csaxs_bec.devices.omny.galil.galil_rio.GalilRIO - deviceConfig: - host: galilrioop.psi.ch - enabled: true - onFailure: retry - readOnly: false - readoutPriority: baseline - connectionTimeout: 20 +# galilrioop: +# description: Galil RIO for remote gain switching and slow reading XBox OP +# deviceClass: csaxs_bec.devices.omny.galil.galil_rio.GalilRIO +# deviceConfig: +# host: galilrioop.psi.ch +# enabled: true +# onFailure: retry +# readOnly: false +# readoutPriority: baseline +# connectionTimeout: 20 -gain_bpm_xbox1: - description: Gain control for BPM XBox1 (OP hutch) - deviceClass: csaxs_bec.devices.pseudo_devices.bpm_control.BPMControl - deviceConfig: - gain_lsb: galilrioop.digital_out.ch0 # Pin 10 -> Galil ch0 - gain_mid: galilrioop.digital_out.ch1 # Pin 11 -> Galil ch1 - gain_msb: galilrioop.digital_out.ch2 # Pin 12 -> Galil ch2 - coupling: galilrioop.digital_out.ch3 # Pin 13 -> Galil ch3 - speed_mode: galilrioop.digital_out.ch4 # Pin 14 -> Galil ch4 - enabled: true - readoutPriority: baseline - onFailure: retry - needs: - - galilrioop +# gain_bpm_xbox1: +# description: Gain control for BPM XBox1 (OP hutch) +# deviceClass: csaxs_bec.devices.pseudo_devices.bpm_control.BPMControl +# deviceConfig: +# gain_lsb: galilrioop.digital_out.ch0 # Pin 10 -> Galil ch0 +# gain_mid: galilrioop.digital_out.ch1 # Pin 11 -> Galil ch1 +# gain_msb: galilrioop.digital_out.ch2 # Pin 12 -> Galil ch2 +# coupling: galilrioop.digital_out.ch3 # Pin 13 -> Galil ch3 +# speed_mode: galilrioop.digital_out.ch4 # Pin 14 -> Galil ch4 +# enabled: true +# readoutPriority: baseline +# onFailure: retry +# needs: +# - galilrioop -bpm_xbox1_slowrb: - description: BPM Xbox 1 (OP hutch) readback - deviceClass: csaxs_bec.devices.pseudo_devices.bpm.BPM - deviceConfig: - left_top: galilrioop.analog_in.ch0 - right_top: galilrioop.analog_in.ch1 - right_bot: galilrioop.analog_in.ch2 - left_bot: galilrioop.analog_in.ch3 - enabled: true - readoutPriority: baseline - onFailure: retry - needs: - - galilrioop +# bpm_xbox1_slowrb: +# description: BPM Xbox 1 (OP hutch) readback +# deviceClass: csaxs_bec.devices.pseudo_devices.bpm.BPM +# deviceConfig: +# left_top: galilrioop.analog_in.ch0 +# right_top: galilrioop.analog_in.ch1 +# right_bot: galilrioop.analog_in.ch2 +# left_bot: galilrioop.analog_in.ch3 +# enabled: true +# readoutPriority: baseline +# onFailure: retry +# needs: +# - galilrioop -gain_diodes_xbox1: - description: Gain control for diodes (horizontal and vertical) XBox1 - deviceClass: csaxs_bec.devices.pseudo_devices.bpm_control.BPMControl - deviceConfig: - gain_lsb: galilrioop.digital_out.ch6 # Pin 10 -> Galil ch0 - gain_mid: galilrioop.digital_out.ch7 # Pin 11 -> Galil ch1 - gain_msb: galilrioop.digital_out.ch8 # Pin 12 -> Galil ch2 - coupling: galilrioop.digital_out.ch9 # Pin 13 -> Galil ch3 - speed_mode: galilrioop.digital_out.ch10 # Pin 14 -> Galil ch4 - enabled: true - readoutPriority: baseline - onFailure: retry - needs: - - galilrioop +# gain_diodes_xbox1: +# description: Gain control for diodes (horizontal and vertical) XBox1 +# deviceClass: csaxs_bec.devices.pseudo_devices.bpm_control.BPMControl +# deviceConfig: +# gain_lsb: galilrioop.digital_out.ch6 # Pin 10 -> Galil ch0 +# gain_mid: galilrioop.digital_out.ch7 # Pin 11 -> Galil ch1 +# gain_msb: galilrioop.digital_out.ch8 # Pin 12 -> Galil ch2 +# coupling: galilrioop.digital_out.ch9 # Pin 13 -> Galil ch3 +# speed_mode: galilrioop.digital_out.ch10 # Pin 14 -> Galil ch4 +# enabled: true +# readoutPriority: baseline +# onFailure: retry +# needs: +# - galilrioop -diode_horizontal_xbox1_slowrb: - description: Slow readback diode horizontal XBox OP (polarization diagnostics) - deviceClass: csaxs_bec.devices.pseudo_devices.signal_forwarder.SignalForwarder - deviceConfig: - signal: galilrioop.analog_in.ch6 - enabled: true - readoutPriority: baseline - onFailure: retry - needs: - - galilrioop +# diode_horizontal_xbox1_slowrb: +# description: Slow readback diode horizontal XBox OP (polarization diagnostics) +# deviceClass: csaxs_bec.devices.pseudo_devices.signal_forwarder.SignalForwarder +# deviceConfig: +# signal: galilrioop.analog_in.ch6 +# enabled: true +# readoutPriority: baseline +# onFailure: retry +# needs: +# - galilrioop -diode_vertical_xbox1_slowrb: - description: Slow readback diode vertical XBox OP (polarization diagnostics) - deviceClass: csaxs_bec.devices.pseudo_devices.signal_forwarder.SignalForwarder - deviceConfig: - signal: galilrioop.analog_in.ch7 - enabled: true - readoutPriority: baseline - onFailure: retry - needs: - - galilrioop +# diode_vertical_xbox1_slowrb: +# description: Slow readback diode vertical XBox OP (polarization diagnostics) +# deviceClass: csaxs_bec.devices.pseudo_devices.signal_forwarder.SignalForwarder +# deviceConfig: +# signal: galilrioop.analog_in.ch7 +# enabled: true +# readoutPriority: baseline +# onFailure: retry +# needs: +# - galilrioop -- 2.52.0 From 6724c8c6f9a101fdaf74719ceecb5b465d29602b Mon Sep 17 00:00:00 2001 From: x12sa Date: Thu, 9 Apr 2026 12:37:13 +0200 Subject: [PATCH 3/5] slits show all and all bl slits to ES config --- .../bec_ipython_client/plugins/cSAXS/cSAXS.py | 2 + .../bec_ipython_client/plugins/cSAXS/slits.py | 107 +++++++++++++++ csaxs_bec/device_configs/bl_endstation.yaml | 128 +++++++++++++++++- 3 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 csaxs_bec/bec_ipython_client/plugins/cSAXS/slits.py diff --git a/csaxs_bec/bec_ipython_client/plugins/cSAXS/cSAXS.py b/csaxs_bec/bec_ipython_client/plugins/cSAXS/cSAXS.py index 859f43f..f24d80d 100644 --- a/csaxs_bec/bec_ipython_client/plugins/cSAXS/cSAXS.py +++ b/csaxs_bec/bec_ipython_client/plugins/cSAXS/cSAXS.py @@ -17,6 +17,7 @@ from csaxs_bec.bec_ipython_client.plugins.cSAXS.smaract import cSAXSSmaract from csaxs_bec.bec_ipython_client.plugins.omny.omny_general_tools import OMNYTools from csaxs_bec.bec_ipython_client.plugins.cSAXS.filter_transmission import cSAXSFilterTransmission from csaxs_bec.bec_ipython_client.plugins.cSAXS.diagnostics import cSAXSDiagnostics +from csaxs_bec.bec_ipython_client.plugins.cSAXS.slits import cSAXSSlits class cSAXSError(Exception): pass @@ -25,6 +26,7 @@ class cSAXS( cSAXSInitSmaractStages, cSAXSSmaract, cSAXSFilterTransmission, + cSAXSSlits, ): def __init__(self, client): self.client = client diff --git a/csaxs_bec/bec_ipython_client/plugins/cSAXS/slits.py b/csaxs_bec/bec_ipython_client/plugins/cSAXS/slits.py new file mode 100644 index 0000000..fdcb35f --- /dev/null +++ b/csaxs_bec/bec_ipython_client/plugins/cSAXS/slits.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +import builtins +from typing import Optional + +# Make dev visible if running inside BEC (same pattern as diagnostics) +if builtins.dict.get("dev") is not None: + dev = builtins.dict.get("dev") + + +class cSAXSSlits: + """ + User-visible slits namespace. + + Provides read-only overview and helper methods for all cSAXS slits. + """ + + def __init__(self, client): + self.client = client + self.device_manager = client.device_manager + + # ------------------------------------------------------------------ + # internal helpers + # ------------------------------------------------------------------ + + def _get_position(self, device_name: str) -> Optional[float]: + """ + Safely return the position of a device or None if unavailable. + """ + try: + dev = self.device_manager.devices[device_name] + return dev.position + except Exception: + return None + + @staticmethod + def _fmt(value: Optional[float]) -> str: + """ + Format numeric values consistently, SPEC-style. + """ + if value is None: + return " --- " + return f"{value:7.4f}" + + # ------------------------------------------------------------------ + # public API + # ------------------------------------------------------------------ + + def show_all(self): + """ + Show all slits with center and width, ordered along the beamline. + Equivalent to the historic SPEC macro slits_show_all. + """ + + print("") + print("========== cSAXS Slits Overview ==========") + print("") + + # -------------------------------------------------------------- + # Slit 1 – frontend (white beam) + # -------------------------------------------------------------- + print("Slit 1 (frontend):") + + ch = self._get_position("sl1xc") + wh = self._get_position("sl1xs") + cv = self._get_position("sl1yc") + wv = self._get_position("sl1ys") + + print( + f" center horizontal = {self._fmt(ch)} " + f"width horizontal = {self._fmt(wh)}" + ) + print( + f" center vertical = {self._fmt(cv)} " + f"width vertical = {self._fmt(wv)}" + ) + print("") + + # -------------------------------------------------------------- + # Slits 3–5 – exposure boxes + # -------------------------------------------------------------- + slit_map = { + 3: "exposure box 1 entrance", + 4: "exposure box 1 exit", + 5: "exposure box 2 exit", + } + + for slit, label in slit_map.items(): + print(f"Slit {slit} ({label}):") + + ch = self._get_position(f"sl{slit}ch") + wh = self._get_position(f"sl{slit}wh") + cv = self._get_position(f"sl{slit}cv") + wv = self._get_position(f"sl{slit}wv") + + print( + f" center horizontal = {self._fmt(ch)} " + f"width horizontal = {self._fmt(wh)}" + ) + print( + f" center vertical = {self._fmt(cv)} " + f"width vertical = {self._fmt(wv)}" + ) + print("") + + print("==========================================") + print("") diff --git a/csaxs_bec/device_configs/bl_endstation.yaml b/csaxs_bec/device_configs/bl_endstation.yaml index c543a76..0d00ebc 100644 --- a/csaxs_bec/device_configs/bl_endstation.yaml +++ b/csaxs_bec/device_configs/bl_endstation.yaml @@ -196,6 +196,68 @@ sl3trxt: # bl_smar_stage to use csaxs reference method. assign number according to axis channel bl_smar_stage: 5 +sl3ch: + description: ESbox1 slit 3 center horizontal + deviceClass: ophyd_devices.devices.virtual_slit.VirtualSlitCenter + deviceConfig: + left_slit: sl3trxi + right_slit: sl3trxo + offset: 0 + enabled: true + onFailure: retry + readOnly: false + readoutPriority: baseline + needs: + - sl3trxi + - sl3trxo + + +sl3wh: + description: ESbox1 slit 3 width horizontal + deviceClass: ophyd_devices.devices.virtual_slit.VirtualSlitWidth + deviceConfig: + left_slit: sl3trxi + right_slit: sl3trxo + offset: 0 + enabled: true + onFailure: retry + readOnly: false + readoutPriority: baseline + needs: + - sl3trxi + - sl3trxo + +sl3cv: + description: ESbox1 slit 3 center vertical + deviceClass: ophyd_devices.devices.virtual_slit.VirtualSlitCenter + deviceConfig: + left_slit: sl3trxb + right_slit: sl3trxt + offset: 0 + enabled: true + onFailure: retry + readOnly: false + readoutPriority: baseline + needs: + - sl3trxb + - sl3trxt + + +sl3wv: + description: ESbox1 slit 3 width vertical + deviceClass: ophyd_devices.devices.virtual_slit.VirtualSlitWidth + deviceConfig: + left_slit: sl3trxb + right_slit: sl3trxt + offset: 0 + enabled: true + onFailure: retry + readOnly: false + readoutPriority: baseline + needs: + - sl3trxb + - sl3trxt + fast_shutter_n1_x: description: ESbox1 New fast shutter 1 x movment deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor @@ -450,7 +512,71 @@ sl4trxt: # bl_smar_stage to use csaxs reference method. assign number according to axis channel bl_smar_stage: 0 -################## XBOX 2 ES ##################### +sl4ch: + description: ESbox1 slit 4 center horizontal + deviceClass: ophyd_devices.devices.virtual_slit.VirtualSlitCenter + deviceConfig: + left_slit: sl4trxi + right_slit: sl4trxo + offset: 0 + enabled: true + onFailure: retry + readOnly: false + readoutPriority: baseline + needs: + - sl4trxi + - sl4trxo + + +sl4wh: + description: ESbox1 slit 4 width horizontal + deviceClass: ophyd_devices.devices.virtual_slit.VirtualSlitWidth + deviceConfig: + left_slit: sl4trxi + right_slit: sl4trxo + offset: 0 + enabled: true + onFailure: retry + readOnly: false + readoutPriority: baseline + needs: + - sl4trxi + - sl4trxo + + +sl4cv: + description: ESbox1 slit 4 center vertical + deviceClass: ophyd_devices.devices.virtual_slit.VirtualSlitCenter + deviceConfig: + left_slit: sl4trxb + right_slit: sl4trxt + offset: 0 + enabled: true + onFailure: retry + readOnly: false + readoutPriority: baseline + needs: + - sl4trxb + - sl4trxt + + +sl4wv: + description: ESbox1 slit 4 width vertical + deviceClass: ophyd_devices.devices.virtual_slit.VirtualSlitWidth + deviceConfig: + left_slit: sl4trxb + right_slit: sl4trxt + offset: 0 + enabled: true + onFailure: retry + readOnly: false + readoutPriority: baseline + needs: + - sl4trxb + - sl4trxt + + +############## XBOX 3 (XBOX 2 ES) ##################### sl5trxi: description: ESbox2 slit 5 inner blade movement -- 2.52.0 From eae28d85b8a265b585e7a4ee58e933b358269720 Mon Sep 17 00:00:00 2001 From: x12sa Date: Thu, 9 Apr 2026 12:51:31 +0200 Subject: [PATCH 4/5] make diagnostics robust against missing devices; add multi-signal support for polarization diodes --- .../plugins/cSAXS/diagnostics.py | 140 ++++++++++++++---- 1 file changed, 114 insertions(+), 26 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/cSAXS/diagnostics.py b/csaxs_bec/bec_ipython_client/plugins/cSAXS/diagnostics.py index 14a8cec..c509a71 100644 --- a/csaxs_bec/bec_ipython_client/plugins/cSAXS/diagnostics.py +++ b/csaxs_bec/bec_ipython_client/plugins/cSAXS/diagnostics.py @@ -1,10 +1,23 @@ from __future__ import annotations import builtins +# ------------------------------------------------------------ +# Access to dev (as before) +# ------------------------------------------------------------ if builtins.__dict__.get("dev") is not None: dev = builtins.__dict__.get("dev") +# ------------------------------------------------------------ +# Safe device access +# ------------------------------------------------------------ +def _safe_dev(name): + try: + return getattr(dev, name) + except Exception: + return None + + # ============================================================ # Base classes # ============================================================ @@ -19,15 +32,25 @@ class SingleSignalDiag: self._signal = signal_dev def show_all(self): - cfg = self._gain.get() + print(self._label) + print("-" * len(self._label)) + + if self._gain is None or self._signal is None: + print("device not available in config\n") + return + + try: + cfg = self._gain.get() + except Exception as e: + print(f"error accessing gain: {e}\n") + return + try: val = self._signal.signal.get() sval = f"{val:.4f}" except Exception: sval = "N/A" - print(self._label) - print("-" * len(self._label)) print(f"gain : {cfg.gain:.0e}") print(f"coupling : {cfg.coupling}") print(f"speed : {cfg.speed}") @@ -35,9 +58,61 @@ class SingleSignalDiag: print("") print("hint:") print(f" gain : csaxs.diagnostics.{self._name}.gain(1e5)") - print(f" raw : dev.{self._signal.name}.signal.get()") + print(f" raw : dev.{getattr(self._signal, 'name', '?')}.signal.get()") + print("") def gain(self, value): + if self._gain is None: + print("gain device not available") + return + self._gain.set_gain(value) + + +class MultiSignalDiag: + """Diagnostics for multiple signals sharing one gain.""" + + def __init__(self, name, label, gain_dev, signal_devs: dict): + self._name = name + self._label = label + self._gain = gain_dev + self._signals = signal_devs # dict: label -> device + + def show_all(self): + print(self._label) + print("-" * len(self._label)) + + if self._gain is None or any(v is None for v in self._signals.values()): + print("device not available in config\n") + return + + try: + cfg = self._gain.get() + except Exception as e: + print(f"error accessing gain: {e}\n") + return + + print(f"gain : {cfg.gain:.0e}") + print(f"coupling : {cfg.coupling}") + print(f"speed : {cfg.speed}") + print("readback:") + + for key, sig in self._signals.items(): + try: + val = sig.signal.get() + sval = f"{val:.4f}" + except Exception: + sval = "N/A" + print(f" {key:<10}: {sval}") + + print("") + print("hint:") + print(f" gain : csaxs.diagnostics.{self._name}.gain(1e5)") + print("") + + def gain(self, value): + if self._gain is None: + print("gain device not available") + return self._gain.set_gain(value) @@ -51,11 +126,20 @@ class BPMDiag: self._rb = rb_dev def show_all(self): - cfg = self._gain.get() - rb = self._rb.get() - print(self._label) print("-" * len(self._label)) + + if self._gain is None or self._rb is None: + print("device not available in config\n") + return + + try: + cfg = self._gain.get() + rb = self._rb.get() + except Exception as e: + print(f"error accessing device: {e}\n") + return + print(f"gain : {cfg.gain:.0e}") print(f"coupling : {cfg.coupling}") print(f"speed : {cfg.speed}") @@ -66,9 +150,13 @@ class BPMDiag: print("") print("hint:") print(f" gain : csaxs.diagnostics.{self._name}.gain(1e6)") - print(f" raw : dev.{self._rb.name}.pos_x.get()") + print(f" raw : dev.{getattr(self._rb, 'name', '?')}.pos_x.get()") + print("") def gain(self, value): + if self._gain is None: + print("gain device not available") + return self._gain.set_gain(value) @@ -84,49 +172,49 @@ class cSAXSDiagnostics: self.bpm_xbox1 = BPMDiag( name="bpm_xbox1", label="BPM XBox1 (OP hutch)", - gain_dev=dev.gain_bpm_xbox1, - rb_dev=dev.bpm_xbox1_slowrb, + gain_dev=_safe_dev("gain_bpm_xbox1"), + rb_dev=_safe_dev("bpm_xbox1_slowrb"), ) self.bpm_xbox2 = BPMDiag( name="bpm_xbox2", label="BPM XBox2 (ES hutch)", - gain_dev=dev.gain_bpm_xbox2, - rb_dev=dev.bpm_xbox2_slowrb, + gain_dev=_safe_dev("gain_bpm_xbox2"), + rb_dev=_safe_dev("bpm_xbox2_slowrb"), ) # Single-signal diagnostics self.bim = SingleSignalDiag( name="bim", label="BIM XBox3 (ES)", - gain_dev=dev.gain_bim_xbox3, - signal_dev=dev.bim_xbox3_slowrb, + gain_dev=_safe_dev("gain_bim_xbox3"), + signal_dev=_safe_dev("bim_xbox3_slowrb"), ) self.beamstop = SingleSignalDiag( name="beamstop", label="Beamstop diode (flight tube)", - gain_dev=dev.gain_beamstop_diode, - signal_dev=dev.beamstop_intensity, + gain_dev=_safe_dev("gain_beamstop_diode"), + signal_dev=_safe_dev("beamstop_intensity"), ) - self.polarization = SingleSignalDiag( + # Multi-signal (fixed your diode issue) + self.polarization = MultiSignalDiag( name="polarization", label="Polarization diodes (XBox1)", - gain_dev=dev.gain_diodes_xbox1, - signal_dev=dev.diode_horizontal_xbox1_slowrb, + gain_dev=_safe_dev("gain_diodes_xbox1"), + signal_devs={ + "horizontal": _safe_dev("diode_horizontal_xbox1_slowrb"), + "vertical": _safe_dev("diode_vertical_xbox1_slowrb"), + }, ) def show_all(self): print("CSAXS diagnostics") - print("=================") - print("") + print("=================\n") + self.bpm_xbox1.show_all() - print("") self.bpm_xbox2.show_all() - print("") self.bim.show_all() - print("") self.beamstop.show_all() - print("") - self.polarization.show_all() + self.polarization.show_all() \ No newline at end of file -- 2.52.0 From a93b737be145c3de819461d15402ae7146f1373f Mon Sep 17 00:00:00 2001 From: x12sa Date: Thu, 9 Apr 2026 13:01:28 +0200 Subject: [PATCH 5/5] fix slits show all --- .../bec_ipython_client/plugins/cSAXS/slits.py | 101 ++++++++++-------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/cSAXS/slits.py b/csaxs_bec/bec_ipython_client/plugins/cSAXS/slits.py index fdcb35f..9a62402 100644 --- a/csaxs_bec/bec_ipython_client/plugins/cSAXS/slits.py +++ b/csaxs_bec/bec_ipython_client/plugins/cSAXS/slits.py @@ -3,105 +3,114 @@ from __future__ import annotations import builtins from typing import Optional -# Make dev visible if running inside BEC (same pattern as diagnostics) -if builtins.dict.get("dev") is not None: - dev = builtins.dict.get("dev") +# ------------------------------------------------------------ +# Make dev visible if running inside BEC +# ------------------------------------------------------------ +if builtins.__dict__.get("dev") is not None: + dev = builtins.__dict__.get("dev") +# ============================================================ +# Slits root (user-facing object) +# ============================================================ + class cSAXSSlits: """ User-visible slits namespace. - - Provides read-only overview and helper methods for all cSAXS slits. """ def __init__(self, client): self.client = client self.device_manager = client.device_manager - # ------------------------------------------------------------------ + # -------------------------------------------------------- # internal helpers - # ------------------------------------------------------------------ + # -------------------------------------------------------- - def _get_position(self, device_name: str) -> Optional[float]: + def _sl_get_position(self, device_name: str) -> Optional[float]: """ - Safely return the position of a device or None if unavailable. + Safely return the position of a device or None. """ try: - dev = self.device_manager.devices[device_name] - return dev.position + device = self.device_manager.devices[device_name] except Exception: return None + try: + if hasattr(device, "readback"): + return device.readback.get() + except Exception: + pass + + try: + if hasattr(device, "get"): + return device.get() + except Exception: + pass + + return None + @staticmethod - def _fmt(value: Optional[float]) -> str: - """ - Format numeric values consistently, SPEC-style. - """ + def _sl_fmt(value: Optional[float]) -> str: if value is None: return " --- " return f"{value:7.4f}" - # ------------------------------------------------------------------ + # -------------------------------------------------------- # public API - # ------------------------------------------------------------------ + # -------------------------------------------------------- - def show_all(self): + def slits_show_all(self): """ - Show all slits with center and width, ordered along the beamline. - Equivalent to the historic SPEC macro slits_show_all. + Show all slits with center and width. """ print("") print("========== cSAXS Slits Overview ==========") print("") - # -------------------------------------------------------------- - # Slit 1 – frontend (white beam) - # -------------------------------------------------------------- + # Slit 1 print("Slit 1 (frontend):") - ch = self._get_position("sl1xc") - wh = self._get_position("sl1xs") - cv = self._get_position("sl1yc") - wv = self._get_position("sl1ys") + ch = self._sl_get_position("sl1xc") + wh = self._sl_get_position("sl1xs") + cv = self._sl_get_position("sl1yc") + wv = self._sl_get_position("sl1ys") print( - f" center horizontal = {self._fmt(ch)} " - f"width horizontal = {self._fmt(wh)}" + f" center horizontal = {self._sl_fmt(ch)} " + f"width horizontal = {self._sl_fmt(wh)}" ) print( - f" center vertical = {self._fmt(cv)} " - f"width vertical = {self._fmt(wv)}" + f" center vertical = {self._sl_fmt(cv)} " + f"width vertical = {self._sl_fmt(wv)}" ) print("") - # -------------------------------------------------------------- - # Slits 3–5 – exposure boxes - # -------------------------------------------------------------- + # Slits 3–5 slit_map = { - 3: "exposure box 1 entrance", - 4: "exposure box 1 exit", - 5: "exposure box 2 exit", + 3: "exposure box 2 entrance", + 4: "exposure box 2 exit", + 5: "exposure box 3", } for slit, label in slit_map.items(): print(f"Slit {slit} ({label}):") - ch = self._get_position(f"sl{slit}ch") - wh = self._get_position(f"sl{slit}wh") - cv = self._get_position(f"sl{slit}cv") - wv = self._get_position(f"sl{slit}wv") + ch = self._sl_get_position(f"sl{slit}ch") + wh = self._sl_get_position(f"sl{slit}wh") + cv = self._sl_get_position(f"sl{slit}cv") + wv = self._sl_get_position(f"sl{slit}wv") print( - f" center horizontal = {self._fmt(ch)} " - f"width horizontal = {self._fmt(wh)}" + f" center horizontal = {self._sl_fmt(ch)} " + f"width horizontal = {self._sl_fmt(wh)}" ) print( - f" center vertical = {self._fmt(cv)} " - f"width vertical = {self._fmt(wv)}" + f" center vertical = {self._sl_fmt(cv)} " + f"width vertical = {self._sl_fmt(wv)}" ) print("") print("==========================================") - print("") + print("") \ No newline at end of file -- 2.52.0