From 2044313864a419a44e0615dab21dc75500f73f27 Mon Sep 17 00:00:00 2001 From: appleb_m Date: Thu, 2 Apr 2026 14:41:14 +0200 Subject: [PATCH] DAQ: changed routing so that there is a mid layer between automation and manual alls of private functions such as mount, loop_center, rotation and raster. Needs some checking and testing. --- src/aare/daq/daq.py | 263 ++++++++++++++++++++++++++++++++------------ 1 file changed, 190 insertions(+), 73 deletions(-) diff --git a/src/aare/daq/daq.py b/src/aare/daq/daq.py index 355a2e37..39b0b4a5 100644 --- a/src/aare/daq/daq.py +++ b/src/aare/daq/daq.py @@ -83,6 +83,116 @@ class AareDAQ: except Exception as e: logger.warning(f"Failed to emit face detection progress: {e}") + def _handle_operation_error(self, operation_name: str, sample: SampleShortInfo | None, error: Exception, + error_type: str = "generic") -> None: + """ + Centralized error handling for all operations. + + Args: + operation_name: Name of the operation that failed (e.g., "mount", "alc", "raster") + sample: The sample being processed + error: The exception that occurred + error_type: Type of error - "mount", "alc", "axc", or "generic" + """ + if sample is None: + logger.error(f"Error in {operation_name}: {error}") + return + + try: + if error_type == "mount": + self.__aare.sample_failed(sample, failed_comment=f"Mount failed: {error}") + elif error_type == "alc": + self.__aare.alc_failed(sample, alc_comment=f"Loop centering failed: {error}") + elif error_type == "axc": + self.__aare.axc_failed(sample) + else: + self.__aare.sample_failed(sample, failed_comment=f"Error in {operation_name}: {error}") + except Exception as db_error: + logger.error(f"Failed to report {operation_name} error to database: {db_error}") + + def _execute_mount_and_prepare(self, sample: SampleShortInfo) -> bool: + """ + Execute mounting and take screenshot. + + Returns: + True if successful, False otherwise + """ + try: + self.__mount(sample) + if sample.db_id is not None: + self.__aare.sample_mounted(sample) + self.save_screenshot_db(sample.db_id, f"{sample.db_id}_mounted") + return True + except Exception as e: + logger.error(f"Mount failed: {e}") + self._handle_operation_error("mount", sample, e, error_type="mount") + return False + + def _execute_loop_centering(self, sample_id: int | None) -> bool: + """ + Execute loop centering sequence. + + Returns: + True if successful, False otherwise + """ + try: + if not self.__loop_center_sequence(sample_id): + self._handle_operation_error("loop_centering", self.sample, LoopCenteringFailed(), error_type="alc") + return False + return True + except Exception as e: + logger.error(f"Loop centering failed: {e}") + self._handle_operation_error("loop_centering", self.sample, e, error_type="alc") + return False + + def _execute_raster_sequence(self, grid_request: RasterGridRequest, + auto_center: bool = False) -> CompletedRasterGrid | None: + """ + Execute raster scan with optional auto-centering. + + Args: + grid_request: Raster grid parameters + auto_center: If True, use auto-centering; if False, use direct raster + + Returns: + CompletedRasterGrid result or None if failed + """ + try: + if auto_center: + result = self.__auto_center(grid_request) + else: + raster_result = self.__raster(grid_request) + result = CompletedRasterGrid(r=[raster_result]) + return result + except Exception as e: + logger.error(f"Raster sequence failed: {e}") + self._handle_operation_error("raster", self.sample, e, error_type="axc") + return None + + def _execute_rotation_sequence(self, rotation_request: RotationScanRequest) -> CompletedRotationScan | None: + """ + Execute rotation scan. + + Args: + rotation_request: Rotation scan parameters + + Returns: + CompletedRotationScan result or None if failed + """ + try: + result = self.__rotation(rotation_request) + self.__set_state(BeamlineStateEnum.SampleAlignment) + if self.sample is not None and self.sample.db_id is not None: + self.save_screenshot_db(self.sample.db_id, "scan_preview") + self.__aare.sample_collected(self.sample) + self.__aare.ingest_scan(sample=self.sample, result=result.result, + geom=self.sample_geometry, beam_mark_pxl=self.__cfg.get_beam_mark(self.zoom)) + return result + except Exception as e: + logger.error(f"Rotation sequence failed: {e}") + self._handle_operation_error("rotation", self.sample, e, error_type="generic") + return None + def _append_smargon_trace(self, *, sample_id: int | None, event: str) -> None: try: path = self._smargon_trace_path @@ -865,24 +975,15 @@ class AareDAQ: """ self.__cfg.try_set_busy(timeout=ceil(360)) try: - if auto: - result = self.__auto_center(r) - else: - raster_result = self.__raster(r) - result = CompletedRasterGrid(r=[raster_result]) - self.__set_state(BeamlineStateEnum.SampleAlignment) + result = self._execute_raster_sequence(r, auto_center=auto) + + if result is None: + raise Exception("Raster scan failed") + self.__cfg.state_busy = False - #return CompletedRasterGrid(r=[]) return result except Exception as e: - try: - self.__aare.axc_failed(self.sample) - except Exception as axc_e: - logger.error(f"Exception while reporting AXC failure: {axc_e}") - if not self.busy: - self.__cfg.try_set_busy(timeout=ceil(360)) - if self.status.state != BeamlineStateEnum.SampleAlignment: - self.__set_state(BeamlineStateEnum.SampleAlignment) + self.__set_state(BeamlineStateEnum.SampleAlignment) self.__cfg.state_busy = False raise e @@ -973,8 +1074,10 @@ class AareDAQ: self.__cfg.try_set_busy(timeout=ceil(total_time + 360)) try: - result = self.__rotation(request) - self.__set_state(BeamlineStateEnum.SampleAlignment) + result = self._execute_rotation_sequence(request) + + if result is None: + raise Exception("Rotation scan failed") self.__cfg.state_busy = False return result except Exception as e: @@ -1516,18 +1619,19 @@ class AareDAQ: self.__cfg.try_set_busy(timeout=360) if sample_id is None: sample_id = self.__cfg.current_sample.db_id - if not self.__loop_center_sequence(sample_id): + + if not self._execute_loop_centering(sample_id): raise LoopCenteringFailed + self.__cfg.state_busy = False - except Exception: + except Exception as e: self.__cfg.zoom_mode = ZoomModeEnum.User - #self.__devs.samcam_settings = self.__cfg.zoom_settings.get_camera_settings(self.zoom) self.__cfg.state_busy = False raise - self.__cfg.zoom_mode = ZoomModeEnum.User - #self.__devs.samcam_settings = self.__cfg.zoom_settings.get_camera_settings(self.zoom) + finally: + self.__cfg.zoom_mode = ZoomModeEnum.User end = time.perf_counter() return end - start @@ -1676,6 +1780,17 @@ class AareDAQ: return default_params, "defaults" + def _end_operation(self, start) -> float: + """ + End an operation and return elapsed time. + + Returns: + (elapsed_seconds, end_time_perf_counter) + """ + self.__cfg.state_busy = False + end = time.perf_counter() + return end - start + def measure(self, sample: SampleShortInfo) -> float: """ Main measurement sequence for a given sample. @@ -1700,79 +1815,81 @@ class AareDAQ: logger.info(f"setting busy at {time.perf_counter() - start}") self.__cfg.try_set_busy(timeout=360) logger.info(f"set busy at {time.perf_counter() - start}") - geom = self.sample_geometry - start_mount=time.perf_counter() - logger.info(f"starting mount {sample.db_id} at {time.ctime()}") - self.__mount(sample) - logger.info(f"mounting done at {time.perf_counter() - start_mount}, total time: {time.perf_counter() - start}") - #self.__devs.smargon_pos - #self.__devs.aerotech_pos = - alc_time = time.perf_counter() - start - logger.info(f"starting alc at {alc_time}") - if not self.__loop_center_sequence(sample.db_id): - self.__aare.alc_failed(sample) - print("alc failed") - self.__cfg.state_busy = False - end = time.perf_counter() - return end - start + if not self._execute_mount_and_prepare(sample): + logger.error("Failed to mount sample") + return self._end_operation(start) + + logger.info(f"mounting done at {time.perf_counter() - start}") + + # Step 2: Loop centering + if not self._execute_loop_centering(sample.db_id): + logger.error("Loop Centering did not succeed") + return self._end_operation(start) logger.info(f"alc done at {time.perf_counter() - start}") + # Step 3: Face detection result = self.__face_detection_sequence(steps=7, step_size=30) self._emit_face_detection_progress(result) logger.info(f"face_detection done at {time.perf_counter() - start}") - #self.zoom = 500 hex_string = secrets.token_hex(3) # 3 bytes = 6 hex characters print(hex_string.upper()) + hex_string = secrets.token_hex(3) raster_params = self.get_auto_raster_params() - if self.__auto_center(RasterGridRequest( + geom = self.sample_geometry + + raster_grid = RasterGridRequest( exp_time_s=raster_params.exp_time_s, file_prefix=sample_prefix + f"_{hex_string}", - smargon_top_left= SmargonCoordinate(), + smargon_top_left=SmargonCoordinate(), n_x=1, n_y=1, dtz=raster_params.dtz, grid_size_mm=Coordinate(x=geom.beam_size_mm.x * 0.5, y=geom.beam_size_mm.y * 0.5), omega_deg=self.omega, - transmission = raster_params.transmission, - )): - logger.info(f"raster scans done at {time.perf_counter() - start}") - params, source = self.get_collection_params(prefer_smart=False) - logger.info(f"Using {source} for data collection: {params}") + transmission=raster_params.transmission, + ) + + raster_result = self._execute_raster_sequence(raster_grid, auto_center=True) + if raster_result is None: + logger.error("Raster result was None") + return self._end_operation(start) + + logger.info(f"raster scans done at {time.perf_counter() - start}") + + # Step 5: Data collection (rotation) + params, source = self.get_collection_params(prefer_smart=False) + logger.info(f"Using {source} for data collection: {params}") + + self.__devs.dtz = params.dtz + start_omega = self.omega + + rotation_request = RotationScanRequest( + start_omega_deg=start_omega, + dtz=params.dtz, + file_prefix="data/" + sample_prefix + f"_{hex_string}", + exp_time_s=params.exp_time_s, + incr_omega_deg=params.incr_omega_deg, + steps=params.steps, + transmission=params.transmission, + ) + + rotation_result = self._execute_rotation_sequence(rotation_request) + if rotation_result is None: + logger.error("Rotation result was None") + return self._end_operation(start) + + logger.info(f"rotation done at {time.perf_counter() - start}") - self.__devs.dtz = params.dtz - start_omega = self.omega - self.__rotation( RotationScanRequest(start_omega_deg=start_omega, - dtz=params.dtz, - file_prefix="data/" + sample_prefix + f"_{hex_string}", - exp_time_s=params.exp_time_s, - incr_omega_deg=params.incr_omega_deg, - steps=params.steps, - transmission=params.transmission, - )) - logger.info(f"rotation done at {time.perf_counter() - start}") - self.__cfg.state_busy = False - end = time.perf_counter() - return end - start - else: - logger.error("auto center failed") - self.__aare.axc_failed(sample) except Exception as e: logger.error(f"Error in measure: {e}") - if isinstance(e, AXCFailed): - logger.debug(f"error is instance AXCFailed") - self.__aare.axc_failed(sample) - else: - self.__aare.sample_failed(sample, f"Error in automation: {e}") - self.__cfg.state_busy = False - end = time.perf_counter() - raise e - self.__cfg.state_busy = False - end = time.perf_counter() - return end - start + self._handle_operation_error("measure", sample, e, error_type="generic") + + finally: + return self._end_operation(start) def __set_state(self, target: BeamlineStateEnum): """__set_state assumes that beamline is already in busy state