diff --git a/daq/src/aaredaq/daq.py b/daq/src/aaredaq/daq.py index 5308e6cb..18aac8c4 100644 --- a/daq/src/aaredaq/daq.py +++ b/daq/src/aaredaq/daq.py @@ -322,6 +322,40 @@ class AareDAQ: else: return None + def __auto_center(self, grid: RasterGridRequest) -> CompletedRasterGrid | None: + sample = self.sample + if sample is None: + raise Exception("Sample must be mounted to auto center") + + self.__devs.aerotech.move(grid.omega_deg, wait=True) + old_prefix = grid.file_prefix + r = self.__ml_bounding_box(sample.db_id, grid.file_prefix) + if r is not None: + grid.smargon = r.smargon + grid.grid_size_mm = r.grid_size_mm + grid.n_x = r.n_x + grid.n_y = r.n_y + grid.file_prefix = f"{old_prefix}_{grid.omega_deg}deg" + + res1 = self.__raster(grid) + + # self.__devs.aerotech.move(grid.omega_deg + 90, wait=True) + self.__set_state(BeamlineStateEnum.SampleAlignment) + return res1 + else: + return None + + def auto_center(self, grid: RasterGridRequest) -> CompletedRasterGrid | None: + try: + self.__cfg.try_set_busy(timeout=360) + result = self.__auto_center(grid) + self.__cfg.state_busy = False + return result + except Exception as e: + self.__set_state(BeamlineStateEnum.SampleAlignment) + self.__cfg.state_busy = False + raise e + def __raster(self, r: RasterGridRequest) -> CompletedRasterGrid: max_time = r.exp_time_s * r.n_y * r.n_x + 60 self.__cfg.dtz = r.dtz @@ -367,11 +401,13 @@ class AareDAQ: self.__devs.smargon.target = save_smargon_position return CompletedRasterGrid(request=copy.deepcopy(r), result=result) - def measure_raster(self, r: RasterGridRequest) -> CompletedRasterGrid: - max_time = r.exp_time_s * r.n_y * r.n_x + 60 - self.__cfg.try_set_busy(timeout=ceil(max_time + 360)) + def measure_raster(self, r: RasterGridRequest, auto: bool) -> CompletedRasterGrid: + self.__cfg.try_set_busy(timeout=ceil(360)) try: - result = self.__raster(r) + if auto: + result = self.__auto_center(r) + else: + result = self.__raster(r) self.__set_state(BeamlineStateEnum.SampleAlignment) self.__cfg.state_busy = False return result @@ -565,7 +601,7 @@ class AareDAQ: def listen_changes(self) -> redis.client.PubSub: return self.__cfg.listen_changes() - def __ml_bounding_box(self, sample_id: int | None = None, filename: str | None = None, move: bool = False) -> BoundingBoxModel | None: + def __ml_bounding_box(self, sample_id: int | None = None, filename: str | None = None) -> RasterGridRequest | None: time.sleep(0.2) # Just to be sure image is stable #curr_image = self.camera_image #box = self.__mlbox.predict(curr_image) @@ -574,42 +610,37 @@ class AareDAQ: if box is None: if filename is not None: cv2.imwrite(f"{filename}_no_detection.jpg", bgr_image) + self.__aare.upload_image(sample_id, f"{filename}_no_detection", bgr_image) return None x1, y1, x2, y2 = box if filename is not None: cv2.rectangle(bgr_image, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.imwrite(f"{filename}.jpg", bgr_image) self.__aare.upload_image(sample_id, filename, bgr_image) - if move: - geom = self.sample_geometry - coord = geom.picture_to_smargon(Coordinate(x=x1, y=y1)) - self.__devs.smargon.target = SmargonCoordinate(sh_mm=coord) - self.__devs.smargon.wait(60) - return BoundingBoxModel(bottom_x=x1, bottom_y=y1, top_x=x2, top_y=y2) - def ml_bounding_box(self, sample_id: int | None = None, filename: str | None = None, move: bool = False) -> RasterGridRequest | None: + geom = self.sample_geometry + + start_coord = geom.picture_to_smargon(Coordinate(x=x1, y=y1)) + grid_size = Coordinate(x=geom.beam_size_mm.x * 0.8, y=geom.beam_size_mm.y * 0.8) + n_x = abs(ceil((x2 - x1) * geom.pixel_in_mm / grid_size.x)) + n_y = abs(ceil((y2 - y1) * geom.pixel_in_mm / grid_size.y)) + + return RasterGridRequest( + exp_time_s=0.02, + transmission=1.0, + smargon= SmargonCoordinate(chi_deg = geom.smargon.chi_deg, + phi_deg= geom.smargon.phi_deg, + sh_mm=start_coord), + n_x=n_x, + n_y=n_y, + grid_size_mm= grid_size, + omega_deg=geom.omega_deg + ) + + def ml_bounding_box(self, sample_id: int | None = None, filename: str | None = None) -> RasterGridRequest | None: try: self.__cfg.try_set_busy(timeout=360) - ml_box = self.__ml_bounding_box(sample_id, filename, move) - r = None - if ml_box is not None: - geom = self.sample_geometry - - start_coord = geom.picture_to_smargon(Coordinate(x=ml_box.bottom_x, y=ml_box.bottom_y)) - grid_size = Coordinate(x=geom.beam_size_mm.x * 0.8, y=geom.beam_size_mm.y * 0.8) - n_x = abs(ceil((ml_box.top_x - ml_box.bottom_x) * geom.pixel_in_mm / grid_size.x)) - n_y = abs(ceil((ml_box.top_y - ml_box.bottom_y) * geom.pixel_in_mm / grid_size.y)) - - r = RasterGridRequest( - exp_time_s=0.05, - smargon= SmargonCoordinate(chi_deg = geom.smargon.chi_deg, - phi_deg= geom.smargon.phi_deg, - sh_mm=start_coord), - n_x=n_x, - n_y=n_y, - grid_size_mm= grid_size, - omega_deg=geom.omega_deg - ) + r = self.__ml_bounding_box(sample_id, filename) self.__cfg.state_busy = False return r except Exception: @@ -748,7 +779,7 @@ class AareDAQ: self.__devs.aerotech.move(0, wait=True) - self.__ml_bounding_box(sample.db_id, f"bb_raster_0", move=True) + self.__ml_bounding_box(sample.db_id, f"bb_raster_0") self.__raster( RasterGridRequest( @@ -763,7 +794,7 @@ class AareDAQ: self.__devs.aerotech.move(90, wait=True) - self.__ml_bounding_box(sample.db_id, f"bb_raster_90", move=True) + self.__ml_bounding_box(sample.db_id, f"bb_raster_90") self.__raster( RasterGridRequest( diff --git a/daq/src/aaredaq/server.py b/daq/src/aaredaq/server.py index fd7aba71..ba425139 100644 --- a/daq/src/aaredaq/server.py +++ b/daq/src/aaredaq/server.py @@ -289,9 +289,9 @@ async def beam_location(token: str = Depends(oauth2_scheme)): # Scans @app.post("/scan/raster") -async def raster(val: RasterGridRequest, token: str = Depends(oauth2_scheme)) -> CompletedRasterGrid: +async def raster(val: RasterGridRequest, auto: bool = False, token: str = Depends(oauth2_scheme)) -> CompletedRasterGrid: auth.check_jwt_rw(cfg, auth.parse_token(token)) - return daq.measure_raster(val) + return daq.measure_raster(val, auto) @app.post("/scan/rotation") diff --git a/gui/src/aaregui/main_window.py b/gui/src/aaregui/main_window.py index b08273b2..0a6a404e 100644 --- a/gui/src/aaregui/main_window.py +++ b/gui/src/aaregui/main_window.py @@ -201,6 +201,7 @@ class MainWindow(QMainWindow): self.sample_camera.evaluate_grid.connect(self.raster.run_grid_scan) self.data_collection.raster.evaluate_grid.connect(self.raster.run_grid_scan) + self.data_collection.raster.evaluate_grid_auto.connect(self.raster.run_grid_scan_auto) self.sample_camera.clear_evaluated_grids.connect(self.raster.clear_completed_grids) self.sample_camera.clear_grid.connect(self.raster.clear_active_grid) @@ -222,6 +223,7 @@ class MainWindow(QMainWindow): self.data_collection.cancel.connect(self.daq.cancel) self.raster.grid_scan.connect(self.daq.raster_scan) + self.raster.grid_scan_auto.connect(self.daq.raster_scan_auto) self.data_collection.screening.rotation_scan.connect(self.daq.standard_scan) self.raster.grid_scan_size_changed.connect(self.data_collection.raster.grid_scan_size_change) diff --git a/gui/src/aaregui/panels/raster_data_collection.py b/gui/src/aaregui/panels/raster_data_collection.py index 0b52cf7d..c6c5c697 100644 --- a/gui/src/aaregui/panels/raster_data_collection.py +++ b/gui/src/aaregui/panels/raster_data_collection.py @@ -13,6 +13,7 @@ class RasterDataCollectionPanel(ScanSettingsPanel): grid_size_updated = Signal(float, float) exp_time_updated = Signal(float) evaluate_grid = Signal() + evaluate_grid_auto = Signal() grid_metric_updated = Signal(RasterGridMetric) raster_alpha_changed = Signal(int) @@ -114,6 +115,11 @@ class RasterDataCollectionPanel(ScanSettingsPanel): self.start_button.clicked.connect(self.evaluate_grid) self._layout.addWidget(self.start_button, 11, 0, 1, 5) + self.auto_button = QPushButton("Feeling lucky") + self.auto_button.setStyleSheet("color: rgb(78, 154, 6);") + self.auto_button.clicked.connect(self.evaluate_grid_auto) + self._layout.addWidget(self.auto_button, 12, 0, 1, 5) + self.update_grid_scan_size() @Slot(float) diff --git a/gui/src/aaregui/scan_logic/raster_grid_manager.py b/gui/src/aaregui/scan_logic/raster_grid_manager.py index 55badfa2..caa8ff0b 100644 --- a/gui/src/aaregui/scan_logic/raster_grid_manager.py +++ b/gui/src/aaregui/scan_logic/raster_grid_manager.py @@ -73,7 +73,10 @@ def normalize_angle(angle_deg: float) -> float: class RasterGridManager(QObject): grid_updated = Signal() + grid_scan = Signal(RasterGridRequest) + grid_scan_auto = Signal(RasterGridRequest) + grid_scan_size_changed = Signal(int, int, float, float) completed_grid_updated = Signal() @@ -309,6 +312,10 @@ class RasterGridManager(QObject): grid = copy.deepcopy(self.__active_grid) self.grid_scan.emit(grid) + @Slot() + def run_grid_scan_auto(self): + grid = copy.deepcopy(self.__active_grid) + self.grid_scan_auto.emit(grid) def spot_ice_ratio(self, obj): if obj.spots_ice is None or obj.spots_low_res is None or obj.spots_low_res <= 0: diff --git a/gui/src/aaregui/threads/daq_worker.py b/gui/src/aaregui/threads/daq_worker.py index c6da977b..91d5783b 100644 --- a/gui/src/aaregui/threads/daq_worker.py +++ b/gui/src/aaregui/threads/daq_worker.py @@ -250,13 +250,46 @@ class DAQWorker(QObject): self.raster_scan_completed.emit(reply) return - request = QNetworkRequest(QUrl(f"{self.__base_url}/scan/raster")) + request = QNetworkRequest(QUrl(f"{self.__base_url}/scan/raster?auto=false")) request.setRawHeader(b"Authorization", f"Bearer {self.__token}".encode("utf-8")) request.setRawHeader(b"Content-Type", b"application/json") body = r.model_dump_json() reply = self.__net_manager.post(request, QByteArray(body.encode("utf-8"))) reply.finished.connect(lambda: self.handle_raster_scan_response(reply)) + @Slot(RasterGridRequest) + def raster_scan_auto(self, r: RasterGridRequest): + self.run_number_incremented.emit() + if self.__base_url is None: + print(f"POST /scan/raster: {r.model_dump_json()}") + image_number = r.get_image_number() + new_copy = copy.deepcopy(r) + + images = [] + for i in range(image_number): + images.append(ScanResultImagesInner( + number=i, + efficiency=1.0, + bkg=random.gauss(3.0, 0.1), + spots=random.randint(0, 250), + index=random.randint(0, 1), + mos=random.uniform(0, 0.1), + b=random.uniform(15.0, 80.0) + )) + + reply = CompletedRasterGrid(request=new_copy, + result=ScanResult(file_prefix=r.file_prefix, images=images)) + self.raster_scan_completed.emit(reply) + return + + request = QNetworkRequest(QUrl(f"{self.__base_url}/scan/raster?auto=true")) + request.setRawHeader(b"Authorization", f"Bearer {self.__token}".encode("utf-8")) + request.setRawHeader(b"Content-Type", b"application/json") + body = r.model_dump_json() + reply = self.__net_manager.post(request, QByteArray(body.encode("utf-8"))) + reply.finished.connect(lambda: self.handle_raster_scan_response(reply)) + + @Slot() def load_spreadsheet(self): if self.__base_url is None: