diff --git a/src/aare/daq/aaredb.py b/src/aare/daq/aaredb.py index 472616e4..5cd69dc9 100644 --- a/src/aare/daq/aaredb.py +++ b/src/aare/daq/aaredb.py @@ -152,18 +152,22 @@ class AareWrapper: logger.debug(f"Response status code: {response.status_code}") @log_timing(logger, "AareDB call") - def upload_jpg(self, sample_id: int, filename: str, jpg_image): + def upload_jpg(self, sample_id: int, filename: str, jpg_image, message: Optional[str] = None): logger.debug(f"jppg_image of type: {type(jpg_image)}") url = f"{self.__host}/protected_router/sample_runner/{sample_id}/upload-images" headers = { "accept": "application/json", "X-Shared-Password": os.getenv("AAREDB_SHARED_PASSWORD") } - response = requests.post(url, - files={'uploaded_file': (filename + ".jpg", jpg_image, "image/jpeg")}, - verify=self.__ssl_ca_cert, - cert=(self.__cert_file, self.__key_file), - headers=headers) + request_kwargs = { + "files": {'uploaded_file': (filename + ".jpg", jpg_image, "image/jpeg")}, + "verify": self.__ssl_ca_cert, + "cert": (self.__cert_file, self.__key_file), + "headers": headers, + } + if message is not None: + request_kwargs["data"] = {"comment": message} + response = requests.post(url, **request_kwargs) logger.debug(f"Response status code: {response.status_code}") @log_timing(logger, "AareDB call") diff --git a/src/aare/daq/operations/rotation/service.py b/src/aare/daq/operations/rotation/service.py index 76d8590b..7b4c855b 100644 --- a/src/aare/daq/operations/rotation/service.py +++ b/src/aare/daq/operations/rotation/service.py @@ -85,6 +85,47 @@ class RotationService: result=scan_result, ) + def _ingest_screening_diffraction(self, sample, scan_result) -> None: + """Pull each screening wedge's diffraction image from JFJoch (with spot + finding + resolution-estimate ring drawn in) and upload it to the DB, + linked to this run's sample. One image per wedge (1/2/4 typically). + Best-effort: a missing/failed image is logged and skipped, never fatal. + """ + images = getattr(scan_result, "images", None) or [] + for idx, img in enumerate(images, start=1): + image_id = getattr(img, "number", None) + if image_id is None: + continue + try: + jpg = self.ctx.deps.jfjoch.get_diffraction_image( + image_id, show_spots=True, show_res_est=True + ) + except Exception: + self.logger.warning( + "Screening diffraction image %s unavailable; skipping", + image_id, + exc_info=True, + ) + continue + + bits = [] + angle = getattr(img, "angle", None) + spots = getattr(img, "spots", None) + res = getattr(img, "res", None) + if angle is not None: + bits.append(f"{angle:.2f}°") + if spots is not None: + bits.append(f"{spots} spots") + if res is not None: + bits.append(f"{res:.2f} Å") + comment = "Screening diffraction (wedge {}{})".format( + idx, (", " + ", ".join(bits)) if bits else "" + ) + filename = f"{sample.db_id}_screening_diffraction_{image_id}" + self.ctx.deps.aare.upload_jpg( + sample.db_id, filename, jpg, message=comment + ) + def run(self, request: RotationScanRequest) -> CompletedRotationScan: sample = self.ctx.sample @@ -112,6 +153,12 @@ class RotationService: self.ctx.settings.preview_filename, ) + # For screening runs, ingest the per-wedge diffraction images. Done + # before the COLLECTED event so they pin to the same sample event the + # run is bound to (the COLLECTING event), matching the preview above. + if request.screening and not self.ctx.deps.cfg.simulated_detector: + self._ingest_screening_diffraction(sample, result.result) + if self.ctx.services.events is not None: self.ctx.services.events.send(sample.db_id, SampleEventType.COLLECTED) diff --git a/src/aare/devices/jfjoch.py b/src/aare/devices/jfjoch.py index 6faf332b..fa5b1270 100644 --- a/src/aare/devices/jfjoch.py +++ b/src/aare/devices/jfjoch.py @@ -298,11 +298,24 @@ class JFJochWrapper: def take_pedestal(self): raise NotImplementedError("take_pedestal is not implemented in DAQ through the JFJoch API yet") - def get_diffraction_image(self, image_id: int, wait_between_retries_s: float = 0.1): + def get_diffraction_image( + self, + image_id: int, + wait_between_retries_s: float = 0.1, + *, + show_spots: bool = False, + show_res_est: bool = False, + show_beam_center: bool = False, + ): last_error = None for _ in range(3): try: - return self.__api.image_buffer_image_jpeg_get(id=image_id, show_spots=False) + return self.__api.image_buffer_image_jpeg_get( + id=image_id, + show_spots=show_spots, + show_res_est=show_res_est, + show_beam_center=show_beam_center, + ) except Exception as e: last_error = e time.sleep(wait_between_retries_s)