diff --git a/src/aare/devices/jfjoch.py b/src/aare/devices/jfjoch.py index 39e078ca..9dc25f29 100644 --- a/src/aare/devices/jfjoch.py +++ b/src/aare/devices/jfjoch.py @@ -1,5 +1,6 @@ import math import time +from enum import Enum import jfjoch_client @@ -9,6 +10,13 @@ from aare.common.models import DAQStatusModel, FluorescenceSpectrumOutputModel from aare.common.raster_grid import RasterGridRequest from aare.common.rotation_scan import RotationScanRequest +class ScanTypeEnum(Enum): + RASTER = "Raster" + XRF = "XRF" + ROTATION = "Rotation" + SCREENING = "Screening" + HELICAL = "Helical" + UNKNOWN = "Unknown" class JFJochWrapper: def __init__(self, bl: MXBeamline): @@ -40,10 +48,14 @@ class JFJochWrapper: status = self.__api.status_get() return status.state == 'Idle' - def measure_rotation(self, - r: RotationScanRequest, - s: DAQStatusModel, - f: FluorescenceSpectrumOutputModel | None = None) -> None: + def __format_dataset_settings( + self, + r: RasterGridRequest | RotationScanRequest, + s: DAQStatusModel, + f: FluorescenceSpectrumOutputModel | None = None, + async_start: bool = True + ) -> jfjoch_client.DatasetSettings: + #common sample parameter intiialisation if s.sample is None: pgroup = "p16371" sample = "unknown_sample" @@ -51,84 +63,113 @@ class JFJochWrapper: pgroup = s.sample.user sample = s.sample.sample_name - if r.screening: - wedge = r.wedge_omega_deg + #raster grid, rotation or screening specific parameter initialisation + wedge = None + if isinstance(r, RasterGridRequest): + if r.n_x == 1: + trigger = 1 + images = r.n_y + else: + trigger = r.n_y + images = r.n_x else: - wedge = None - - goniometer_settings = jfjoch_client.RotationAxis( - step=r.incr_omega_deg, - start=r.start_omega_deg, - name="omega", - vector= [-1,0,0], - screening_wedge_deg = wedge - ) - - if r.screening: - images = 1 - trigger = r.steps - else: - images = r.steps - trigger = 1 - - xrf = None - if f is not None: - xrf = jfjoch_client.DatasetSettingsXrayFluorescenceSpectrum( - energy_eV=f.energy_eV, - data=f.spectrum - ) + if r.screening: + wedge = r.wedge_omega_deg + images = 1 + trigger = r.steps + else: + wedge = None + images = r.steps + trigger = 1 + #build common dataset settings dataset_settings = jfjoch_client.DatasetSettings( 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=s.diffraction.dtz_mm, - file_prefix=f"{pgroup}/raw/{r.file_prefix}", + file_prefix=f"{pgroup}/raw/raster/{r.file_prefix}", 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, transmission=s.bl.transmission, ring_current_mA=s.bl.ring_current_mA, sample_temperature_K=s.bl.cryojet_K, + total_flux=s.bl.flux_ph_s, poni_rot1_rad=s.diffraction.poni_rot1_rad, poni_rot2_rad=s.diffraction.poni_rot2_rad, - total_flux=s.bl.flux_ph_s, max_spot_count=1000, detect_ice_rings=True, - xray_fluorescence_spectrum=xrf + async_start=async_start ) + + #settup grid or goniometer settings adn add to dataset settings + if isinstance(r, RasterGridRequest): + grid_settings = jfjoch_client.GridScan( + n_fast=r.n_x, + snake=True, + step_x_um=r.grid_size_mm.x * 1000.0, + step_y_um=r.grid_size_mm.y * 1000.0, + vertical=False + ) + dataset_settings.grid_scan = grid_settings + + else: + goniometer_settings = jfjoch_client.RotationAxis( + step=r.incr_omega_deg, + start=r.start_omega_deg, + name="omega", + vector=[-1, 0, 0], + screening_wedge_deg=wedge + ) + dataset_settings.goniometer = goniometer_settings + + #if measuring fluoresence, add to dataset settings + if f is not None: + xrf = jfjoch_client.DatasetSettingsXrayFluorescenceSpectrum( + energy_eV=f.energy_eV, + data=f.spectrum + ) + dataset_settings.xray_fluorescence_spectrum = xrf + + return dataset_settings + + def __start_scan( + self, + scan_type: ScanTypeEnum, + r: RasterGridRequest | RotationScanRequest, + s: DAQStatusModel, + f: FluorescenceSpectrumOutputModel | None = None, + async_start: bool = True + ): + dataset_settings = self.__format_dataset_settings(r, s, f, async_start=async_start) try: self.__api.start_post(dataset_settings=dataset_settings) except Exception as e: - scan_type = "rotation" - if r.screening: - scan_type = "screening" raise JFJochCommunicationError( - f"JFJoch data collection failed to initialize for {scan_type} scan", + f"JFJoch data collection failed to initialize for {scan_type.value} scan with exception: {e}", operation="POST", endpoint="start_post", base_url=self.__url, ) from e + def measure_rotation(self, + r: RotationScanRequest, + s: DAQStatusModel, + f: FluorescenceSpectrumOutputModel | None = None, + async_start:bool = True) -> None: + if r.screening: + self.__start_scan(ScanTypeEnum.SCREENING, r, s, f, async_start=async_start) + else: + self.__start_scan(ScanTypeEnum.ROTATION, r, s, f, async_start=async_start) + def measure_raster(self, r: RasterGridRequest, - s: DAQStatusModel): - if s.sample is None: - pgroup = "p16371" - sample = "unknown_sample" - else: - pgroup = s.sample.user - sample = s.sample.sample_name - - if r.n_x == 1: - trigger = 1 - images = r.n_y - else: - trigger = r.n_y - images = r.n_x + s: DAQStatusModel, + async_start: bool = True): + self.__start_scan(ScanTypeEnum.RASTER, r, s, async_start=async_start) def wait_till_running(self, timeout: int | float = 60): if self.__simulated: @@ -174,4 +215,62 @@ class JFJochWrapper: except Exception as e: last_error = e time.sleep(wait_between_retries_s) - raise last_error \ No newline at end of file + raise last_error + +if __name__ == "__main__": + import logging + from aare.common.coordinate import SmargonCoordinate, Coordinate + from aare.common.models import SessionStatus, BeamlineStateEnum, SampleCameraSettings, BeamlineStatus + from aare.common.sample_geometry import SampleGeometryModel + from aare.common.diffraction_geometry import DiffractionGeometry + logging.basicConfig(level=logging.DEBUG) + bl = MXBeamline.X10SA + wrapper = JFJochWrapper(bl) + #wrapper.initialize() + #wrapper.wait_till_done(timeout=360) + status = DAQStatusModel( + bl=BeamlineStatus( + name="X10SA", + ring_current_mA=400.0, + flux_ph_s=1e12, + transmission=1.0, + cryojet_K=100.0, + front_light=50.0, + back_light=50.0, + shutter_open=False, + exp_shutter_open=False, + sample_camera=SampleCameraSettings(gain=1.0, exposure=0.02), + zoom=1.0, + commissioning_mode=False, + dtz_min=120.0, + dtz_max=1600.0 + ), + diffraction=DiffractionGeometry( + detector_description="EIGER", + detector_serial_number="123", + dtz_mm=200.0, + pixel_size_mm=0.075, + energy_keV=12.658, + beam_center_pxl=(1000.0, 1000.0), + detector_size_pxl=(4000, 4000), + poni_rot1_rad=0.0, + poni_rot2_rad=0.0 + ), + geom=SampleGeometryModel( + beam_location_pxl=Coordinate(x=1000, y=1000), + pixel_in_mm=0.001, + aerotech=Coordinate(x=0, y=0), + aerotech_meas=Coordinate(x=0, y=0), + smargon=SmargonCoordinate(sh_mm=Coordinate(x=0,y=0,z=0), phi_deg=0, chi_deg=0), + omega_deg=10.5, + beam_size_mm=Coordinate(x=0.02, y=0.01) + ), + session=SessionStatus(), + state=BeamlineStateEnum.Maintenance, + busy=False + ) + wrapper.measure_raster(RasterGridRequest(n_x=1, n_y=1, grid_size_mm=Coordinate(x=0.05,y=0.05), file_prefix="test", exp_time_s=0.1,dtz=150,transmission=1,smargon_top_left=SmargonCoordinate(),omega_deg=0.0), status) + print("waiting for detector to start") + wrapper.wait_till_running() + print("success we can measure") + wrapper.cancel() \ No newline at end of file