def _update_handler(self, metadata):
        # as a first step, try to set the detector_name, skip if detector_name is empty
        detector_name = metadata.get("detector_name")
        if detector_name and detector_name.startswith("JF"):
            # check if jungfrau data handler is already set for this detector
            if self.handler is None or self.handler.detector_name != detector_name:
                try:
                    self.handler = JFDataHandler(detector_name)
                except KeyError:
                    logging.exception(
                        f"Error creating data handler for detector {detector_name}"
                    )
                    self.handler = None
                    return
        else:
            self.handler = None
            return

        # gain file
        gain_file = metadata.get("gain_file", "")
        try:
            self.handler.gain_file = gain_file
        except Exception:
            logging.exception(f"Error loading gain file {gain_file}")
            self.handler.gain_file = ""

        # pedestal file
        pedestal_file = metadata.get("pedestal_file", "")
        try:
            self.handler.pedestal_file = pedestal_file
        except Exception:
            logging.exception(f"Error loading pedestal file {pedestal_file}")
            self.handler.pedestal_file = ""

        # module map
        module_map = metadata.get("module_map")
        self.handler.module_map = None if (
            module_map is None) else np.array(module_map)

        # highgain
        daq_rec = metadata.get("daq_rec")
        self.handler.highgain = False if (daq_rec is None) else bool(daq_rec
                                                                     & 0b1)
def test_handler_init_fail(detector_name):
    with pytest.raises(KeyError):
        JFDataHandler(detector_name)
def _empty_handler():
    empty_handler = JFDataHandler(DETECTOR_NAME)

    yield empty_handler
class StreamAdapter:
    def __init__(self):
        # a placeholder for jf data handler to be initiated with detector name
        self.handler = None

    def process(self,
                image,
                metadata,
                mask=True,
                gap_pixels=True,
                double_pixels="keep",
                geometry=True):
        """Perform jungfrau detector data processing on an image received via stream.

        Args:
            image (ndarray): An image to be processed.
            metadata (dict): A corresponding image metadata.

        Returns:
            ndarray: Resulting image.
        """
        # Eiger workaround
        detector_name = metadata.get("detector_name")
        if detector_name and re.match("(^[A-Za-z]*).EG([0-9A-Za-z]*)",
                                      detector_name):
            return np.copy(np.flipud(image))

        # parse metadata
        self._update_handler(metadata)

        if self.handler is None or image.dtype != np.uint16:
            return image.astype(np.float32, copy=True)

        # skip conversion step if jungfrau data handler cannot do it, thus avoiding Exception raise
        conversion = self.handler.can_convert()
        proc_image = self.handler.process(
            image,
            conversion=conversion,
            mask=False,
            gap_pixels=gap_pixels,
            double_pixels=double_pixels,
            geometry=geometry,
        )

        if mask:
            proc_image = self._apply_mask(proc_image, gap_pixels,
                                          double_pixels, geometry)

        return proc_image

    def get_gains(self,
                  image,
                  metadata,
                  mask=True,
                  gap_pixels=True,
                  double_pixels="keep",
                  geometry=True):
        # parse metadata
        self._update_handler(metadata)

        if self.handler is None or image.dtype != np.uint16:
            return np.zeros((2, 2), dtype="float32")

        gains = self.handler.get_gains(image,
                                       mask=mask,
                                       gap_pixels=gap_pixels,
                                       double_pixels=double_pixels,
                                       geometry=geometry)

        if mask:
            if double_pixels == "interp":
                double_pixels = "keep"
            gains = self._apply_mask(gains, gap_pixels, double_pixels,
                                     geometry)

        return gains

    def _update_handler(self, metadata):
        # as a first step, try to set the detector_name, skip if detector_name is empty
        detector_name = metadata.get("detector_name")
        if detector_name and detector_name.startswith("JF"):
            # check if jungfrau data handler is already set for this detector
            if self.handler is None or self.handler.detector_name != detector_name:
                try:
                    self.handler = JFDataHandler(detector_name)
                except KeyError:
                    logging.exception(
                        f"Error creating data handler for detector {detector_name}"
                    )
                    self.handler = None
                    return
        else:
            self.handler = None
            return

        # gain file
        gain_file = metadata.get("gain_file", "")
        try:
            self.handler.gain_file = gain_file
        except Exception:
            logging.exception(f"Error loading gain file {gain_file}")
            self.handler.gain_file = ""

        # pedestal file
        pedestal_file = metadata.get("pedestal_file", "")
        try:
            self.handler.pedestal_file = pedestal_file
        except Exception:
            logging.exception(f"Error loading pedestal file {pedestal_file}")
            self.handler.pedestal_file = ""

        # module map
        module_map = metadata.get("module_map")
        self.handler.module_map = None if (
            module_map is None) else np.array(module_map)

        # highgain
        daq_rec = metadata.get("daq_rec")
        self.handler.highgain = False if (daq_rec is None) else bool(daq_rec
                                                                     & 0b1)

    def _apply_mask(self, image, gap_pixels, double_pixels, geometry):
        # assign masked values to np.nan
        if self.handler.pixel_mask is not None:
            mask = self.handler.get_pixel_mask(gap_pixels=gap_pixels,
                                               double_pixels=double_pixels,
                                               geometry=geometry)

            # cast to np.float32 in case there was no conversion, but mask should still be applied
            if image.dtype != np.float32:
                image = image.astype(np.float32)

            if image.shape == mask.shape:
                _apply_mask_njit(image, mask)
            else:
                raise ValueError("Image and mask shapes are not the same")

        return image