From 8c517d4bab0fd488d8b71c7f666c00db20d775f5 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Wed, 18 Jun 2025 13:02:34 +0200 Subject: [PATCH] DAQ: Modifications of June 18th --- common/src/aaredaqlib/models.py | 1 + daq/src/aaredaq/aaredb.py | 4 +- daq/src/aaredaq/daq.py | 25 ++++++--- daq/src/aaredaq/devices.py | 5 +- daq/src/mxlibs3/filter_transmission.py | 54 +++++++++++++++++++ daq/src/mxlibs3/jfjoch.py | 42 +++++++-------- gui/src/aaregui/main_window.py | 1 + .../panels/data_collection_settings.py | 1 + gui/src/aaregui/panels/file_path_panel.py | 27 +++++++++- .../aaregui/scan_logic/raster_grid_manager.py | 8 +++ gui/src/aaregui/widgets/status_bar.py | 7 +++ 11 files changed, 142 insertions(+), 33 deletions(-) create mode 100644 daq/src/mxlibs3/filter_transmission.py diff --git a/common/src/aaredaqlib/models.py b/common/src/aaredaqlib/models.py index 9511c1a4..687e7ea3 100644 --- a/common/src/aaredaqlib/models.py +++ b/common/src/aaredaqlib/models.py @@ -153,6 +153,7 @@ class BeamlineStatus(BaseModel): shutter_open: bool flux_ph_s: float sample_camera: SampleCameraSettings + transmission: Annotated[float, Field(ge=0.0, le=1.0)] | None class SessionStatus(BaseModel): session: SessionsStateEnum = SessionsStateEnum.Vacant diff --git a/daq/src/aaredaq/aaredb.py b/daq/src/aaredaq/aaredb.py index 6766b782..589590da 100644 --- a/daq/src/aaredaq/aaredb.py +++ b/daq/src/aaredaq/aaredb.py @@ -249,7 +249,7 @@ class AareWrapper: ringCurrent_mA=d.bl.ring_current_mA, ringMode="Beamline Development", monochromator="Si111", - transmission=r.transmission, + transmission=d.bl.transmission, focusingOptic="Kirkpatrick-Baez", beamlineFluxAtSample_ph_s=d.bl.flux_ph_s, beamSizeWidth=d.geom.beam_size_mm.x * 100, @@ -312,7 +312,7 @@ class AareWrapper: ringCurrent_mA=d.bl.ring_current_mA, ringMode="Beamline Development", monochromator="Si111", - transmission=r.transmission, + transmission=d.bl.transmission, focusingOptic="Kirkpatrick-Baez", beamlineFluxAtSample_ph_s=d.bl.flux_ph_s, beamSizeWidth=d.geom.beam_size_mm.x * 100, diff --git a/daq/src/aaredaq/daq.py b/daq/src/aaredaq/daq.py index 79d348cb..f62b3a8a 100644 --- a/daq/src/aaredaq/daq.py +++ b/daq/src/aaredaq/daq.py @@ -253,12 +253,17 @@ class AareDAQ: max_time = r.exp_time_s * r.n_y * r.n_x + 60 self.__cfg.dtz = r.dtz self.__set_state(BeamlineStateEnum.DataCollection) - self.__jfjoch.measure_raster(r, self.diffraction_geometry, self.sample) - self.__devs.aerotech.move(r.omega_deg, wait=True, speed=360.0) + save_smargon_position = self.__devs.smargon.readback self.__devs.smargon.target = r.smargon + self.__devs.transmission.set(r.transmission, wait=False) + self.__devs.aerotech.move(r.omega_deg, wait=True, speed=360.0) + self.__devs.transmission.wait() + self.__devs.smargon.wait() - self.__aare.create_gridscan_run(self.sample, r, self.status) + status = self.status + self.__jfjoch.measure_raster(r, status) + self.__aare.create_gridscan_run(self.sample, r, status) self.__devs.aerotech.measure_raster_simple( r.exp_time_s, r.grid_size_mm.x, r.grid_size_mm.y, r.n_x, r.n_y @@ -287,13 +292,18 @@ class AareDAQ: self.__cfg.dtz = request.dtz self.__set_state(BeamlineStateEnum.DataCollection) + self.__devs.transmission.set(request.transmission, wait=False) if request.start is not None: self.__devs.smargon.target = request.start + + self.__devs.transmission.wait() + if request.start is not None: self.__devs.smargon.wait() - self.__jfjoch.measure_rotation(request, self.diffraction_geometry, self.sample) - self.__aare.create_rotation_run(self.sample, request, self.status) - + status = self.status + self.__jfjoch.measure_rotation(request, status) + self.__aare.create_rotation_run(self.sample, request, status) + if request.screening: self.__devs.aerotech.measure_screening(request.start_omega_deg, request.wedge_omega_deg, @@ -767,7 +777,8 @@ class AareDAQ: shutter_open=self.__devs.shutter, flux_ph_s=3.0e12, sample_camera=self.__devs.samcam_settings, - name=self.__bl + name=self.__bl, + transmission=self.__devs.transmission.get() ) @property diff --git a/daq/src/aaredaq/devices.py b/daq/src/aaredaq/devices.py index 79b497db..afd8201d 100644 --- a/daq/src/aaredaq/devices.py +++ b/daq/src/aaredaq/devices.py @@ -6,6 +6,7 @@ from aaredaqlib.models import SampleCameraSettings from mxlibs3 import aerotech, smargon from mxlibs3.area_detector import epicsAD from mxlibs3.enum_pv import EnumPv +from mxlibs3.filter_transmission import FilterTransmission from mxlibs3.my_motor import MyMotor from mxlibs3.non_standard import NonStandard from mxlibs3.tell_client import TellClient @@ -59,6 +60,8 @@ class BeamlineDevices: move_done_when=(f"{BEAMLINE}-ES-DF1:GMX-DONE", 1), ) + self.transmission = FilterTransmission(beamline) + self.gmy = NonStandard( name="GMY", setpv=f"{BEAMLINE}-ES-DF1:GMY-VAL", @@ -259,4 +262,4 @@ class BeamlineDevices: @samcam_settings.setter def samcam_settings(self, settings: SampleCameraSettings): print(settings) - self.sample_cam.setup(settings.gain, settings.exposure) \ No newline at end of file + self.sample_cam.setup(settings.gain, settings.exposure) diff --git a/daq/src/mxlibs3/filter_transmission.py b/daq/src/mxlibs3/filter_transmission.py new file mode 100644 index 00000000..3c9c9e17 --- /dev/null +++ b/daq/src/mxlibs3/filter_transmission.py @@ -0,0 +1,54 @@ +import time + +from epics import PV, poll + +from aaredaqlib.beamline import MXBeamline + + +class FilterTransmission: + def __init__(self, bl: MXBeamline, timeout=60.0, fail_on_timeout=True): + BEAMLINE = bl.value.upper() + self.device_name = "FilterTransmission" + self._fail_on_timeout = fail_on_timeout + self._timeout = timeout + + self.__set = PV(f"{BEAMLINE}-ES-FI:TRANSM-SET") + self.__get = PV(f"{BEAMLINE}-ES-FI:TRANSM-GET") + self.__done = PV(f"{BEAMLINE}-ES-FI:DONE") + + def __repr__(self): + return f"" + + def __str__(self): + return f"{self.__get.get():.4f}" + + def is_done(self): + return 1 == int(self.__done.value) + + def is_busy(self): + return not self.is_done() + + def set(self, val, wait=False): + val = float(val) + if not (0.0 <= val <= 1.0): + raise RuntimeError( + f"requested transmission ({val:.4f}) is out of bounds --- valid range : 0 <= trans <= 1.0" + ) + self.__set.put(val) + poll(0.2) + if wait: + self.wait() + + def get(self) -> float | None: + if self.is_busy(): + return None + return round(self.__get.value, 5) + + def wait(self): + timeisup = False + timeout = self._timeout + time.time() + while not timeisup and self.is_busy(): + timeisup = time.time() > timeout + poll(0.1) + if timeisup and self._fail_on_timeout: + raise RuntimeError("timeout waiting for filters to achieve requested transmission.") diff --git a/daq/src/mxlibs3/jfjoch.py b/daq/src/mxlibs3/jfjoch.py index ca78e517..06b1f777 100644 --- a/daq/src/mxlibs3/jfjoch.py +++ b/daq/src/mxlibs3/jfjoch.py @@ -4,7 +4,7 @@ import jfjoch_client from aaredaqlib.beamline import MXBeamline from aaredaqlib.diffraction_geometry import DiffractionGeometry -from aaredaqlib.models import SampleShortInfo +from aaredaqlib.models import SampleShortInfo, DAQStatusModel from aaredaqlib.raster_grid import RasterGridRequest from aaredaqlib.rotation_scan import RotationScanRequest @@ -34,14 +34,13 @@ class JFJochWrapper: def measure_rotation(self, r: RotationScanRequest, - d: DiffractionGeometry, - s: SampleShortInfo | None) -> None: - if s is None: + s: DAQStatusModel) -> None: + if s.sample is None: pgroup = "p16371" sample = None else: - pgroup = s.user - sample = s.sample_name + pgroup = s.sample.user + sample = s.sample.sample_name goniometer_settings = jfjoch_client.RotationAxis( step=r.incr_omega_deg, @@ -58,30 +57,30 @@ class JFJochWrapper: trigger = 1 dataset_settings = jfjoch_client.DatasetSettings( - beam_x_pxl=d.beam_center_pxl[0], - beam_y_pxl=d.beam_center_pxl[1], + beam_x_pxl=s.diffraction.beam_center_pxl[0], + beam_y_pxl=s.diffraction.beam_center_pxl[1], ntrigger= trigger, images_per_trigger=images, - detector_distance_mm=d.dtz_mm, + detector_distance_mm=s.diffraction.dtz_mm, file_prefix=f"{pgroup}/raw/{r.file_prefix}", - incident_energy_keV=d.energy_keV, + incident_energy_keV=s.diffraction.energy_keV, goniometer=goniometer_settings, sample_name=sample, image_time_us=round(r.exp_time_s * 1e6), - experiment_group=pgroup + experiment_group=pgroup, + transmission=s.bl.transmission ) self.__api.start_post(dataset_settings=dataset_settings) def measure_raster(self, r: RasterGridRequest, - d: DiffractionGeometry, - s: SampleShortInfo | None): - if s is None: + s: DAQStatusModel): + if s.sample is None: pgroup = "p16371" sample = None else: - pgroup = s.user - sample = s.sample_name + pgroup = s.sample.user + sample = s.sample.sample_name grid_settings = jfjoch_client.GridScan( n_fast= r.n_x, @@ -92,17 +91,18 @@ class JFJochWrapper: ) dataset_settings = jfjoch_client.DatasetSettings( - beam_x_pxl=d.beam_center_pxl[0], - beam_y_pxl=d.beam_center_pxl[1], + beam_x_pxl=s.diffraction.beam_center_pxl[0], + beam_y_pxl=s.diffraction.beam_center_pxl[1], ntrigger= r.n_y, images_per_trigger=r.n_x, - detector_distance_mm=d.dtz_mm, + detector_distance_mm=s.diffraction.dtz_mm, file_prefix=f"{pgroup}/raw/{r.file_prefix}", - incident_energy_keV=d.energy_keV, + incident_energy_keV=s.diffraction.energy_keV, grid_scan=grid_settings, sample_name=sample, image_time_us=round(r.exp_time_s * 1e6), - experiment_group=pgroup + experiment_group=pgroup, + transmission=s.bl.transmission ) self.__api.start_post(dataset_settings=dataset_settings) diff --git a/gui/src/aaregui/main_window.py b/gui/src/aaregui/main_window.py index c441e2f1..b9085776 100644 --- a/gui/src/aaregui/main_window.py +++ b/gui/src/aaregui/main_window.py @@ -133,6 +133,7 @@ class MainWindow(QMainWindow): self.daq.update.connect(self.beamline.abr_tweak.update_daq_status) self.daq.update.connect(self.status_bar.update_daq_status) self.daq.update.connect(self.beamline.samcam.update_daq_status) + self.daq.raster_scan_completed.connect(self.raster.grid_scan_completed) self.beamline.samcam.changed.connect(self.daq.samcam_settings) diff --git a/gui/src/aaregui/panels/data_collection_settings.py b/gui/src/aaregui/panels/data_collection_settings.py index ec1b3dd5..4d377102 100644 --- a/gui/src/aaregui/panels/data_collection_settings.py +++ b/gui/src/aaregui/panels/data_collection_settings.py @@ -68,3 +68,4 @@ class DataCollectionSettings(QFrame): @Slot(DAQStatusModel) def update_daq_status(self, s: DAQStatusModel): self.raster.update_daq_status(s) + self.file_path_panel.update_daq_status(s) diff --git a/gui/src/aaregui/panels/file_path_panel.py b/gui/src/aaregui/panels/file_path_panel.py index dc858d05..8a4c469d 100644 --- a/gui/src/aaregui/panels/file_path_panel.py +++ b/gui/src/aaregui/panels/file_path_panel.py @@ -3,6 +3,7 @@ from datetime import datetime from PySide6.QtCore import Signal, Slot, Qt from PySide6.QtWidgets import QWidget, QGridLayout, QLabel, QLineEdit, QSpinBox, QCheckBox +from aaredaqlib.models import DAQStatusModel from aaregui.widgets.title_label import TitleLabel @@ -12,6 +13,9 @@ class FilePathPanel(QWidget): def __init__(self, parent=None): super().__init__(parent) grid_layout = QGridLayout(self) + self.__sample_name = "sample" + self.__sample_id = -1 + self.__dewar_pos = "None" self.__filename = "" self.__formatted_date = datetime.now().strftime('%y%m%d') @@ -30,7 +34,7 @@ class FilePathPanel(QWidget): grid_layout.addWidget(self.directory_edit, 2, 1) grid_layout.addWidget(QLabel("File prefix", parent=self), 3, 0) - self.file_prefix_edit = QLineEdit(parent=self) + self.file_prefix_edit = QLineEdit("{sample_name}", parent=self) self.file_prefix_edit.setStyleSheet("background-color: rgb(255, 255, 255);") grid_layout.addWidget(self.file_prefix_edit, 3, 1) @@ -78,6 +82,8 @@ class FilePathPanel(QWidget): self.__filename += f"_{run_number:03d}" self.__filename = self.__filename.replace('{date}', self.__formatted_date) + self.__filename = self.__filename.replace('{sample_name}', self.__sample_name) + self.__filename = self.__filename.replace('{sample_id}', f"{self.__sample_id}") self.file_name_label.setText(self.__filename + "_master.h5") self.file_name_label.setStyleSheet("color: rgb(0, 0, 0);") @@ -106,4 +112,21 @@ class FilePathPanel(QWidget): @property def filename(self) -> str: - return self.__filename \ No newline at end of file + return self.__filename + + @Slot(DAQStatusModel) + def update_daq_status(self, status: DAQStatusModel): + if status.sample is None: + self.__sample_id = -1 + self.__sample_name = "sample" + self.__dewar_pos = "None" + else: + self.__sample_name = status.sample.sample_name + self.__dewar_pos = status.sample.loc_str() + + self.update_filename() + + sample_id_old = self.__sample_id + self.__sample_id = status.sample.db_id + if self.__sample_id != sample_id_old: + self.run_number_edit.setValue(1) \ No newline at end of file diff --git a/gui/src/aaregui/scan_logic/raster_grid_manager.py b/gui/src/aaregui/scan_logic/raster_grid_manager.py index 2af7e02f..fc2cc95d 100644 --- a/gui/src/aaregui/scan_logic/raster_grid_manager.py +++ b/gui/src/aaregui/scan_logic/raster_grid_manager.py @@ -75,6 +75,9 @@ class RasterGridManager(QObject): def __init__(self, geom): super().__init__() + + self.__sample_id = -1 + self.__geom = geom self.__metric = RasterGridMetric.SPOTS @@ -109,6 +112,11 @@ class RasterGridManager(QObject): @Slot(DAQStatusModel) def update_daq_status(self, s: DAQStatusModel): + if s.sample is not None and s.sample.db_id != self.__sample_id: + self.__sample_id = s.sample.db_id + self.clear_completed_grids() + self.clear_active_grid() + self.__geom = s.geom def resize_active_grid(self, end_point: QPointF): diff --git a/gui/src/aaregui/widgets/status_bar.py b/gui/src/aaregui/widgets/status_bar.py index 065c5679..a2155751 100644 --- a/gui/src/aaregui/widgets/status_bar.py +++ b/gui/src/aaregui/widgets/status_bar.py @@ -21,6 +21,7 @@ class StatusBar(QStatusBar): super().__init__(parent) self.__decoded_token = token + self.transmission = ValueLabel("Transmission", "", self) self.ring_current = ValueLabel("Ring current", "mA", self) self.wvl = ValueLabel("Wavelength", "Å", self) self.cryo_label = ValueLabel("Cryo", "K", self) @@ -39,6 +40,7 @@ class StatusBar(QStatusBar): self.shutter_label = ClickableLabel(parent=self) self.shutter_label.clicked.connect(self.show_shutter_menu) + self.addPermanentWidget(self.transmission) self.addPermanentWidget(self.ring_current) self.addPermanentWidget(self.wvl) self.addPermanentWidget(self.cryo_label) @@ -51,6 +53,11 @@ class StatusBar(QStatusBar): @Slot(DAQStatusModel) def update_daq_status(self, status: DAQStatusModel): + if status.bl.transmission is None: + self.transmission.set_value(f"(moving)") + else: + self.transmission.set_value(f"{status.bl.transmission:.5f}") + if status.bl.ring_current_mA < 5.0: self.ring_current.set_value(f"{status.bl.ring_current_mA:.2f}", "red") elif status.bl.ring_current_mA < 390.0: