Commit c9d77b88 authored by vertighel's avatar vertighel
Browse files

Unk

parent 390d81d5
Loading
Loading
Loading
Loading
Loading
+41 −33
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ from AtikSDK import (ARTEMIS_OK, ARTEMIS_PROPERTIES_CAMERAFLAGS_HAS_SHUTTER,
# Custom modules
from .basedevice import BaseDevice
from ..config.constants import camera_frame
from ..utils.check import timed_lock
from ..utils.image import make_png
from ..utils.logger import log

@@ -82,7 +83,8 @@ class Camera(BaseDevice):
            True if connected (already, or just now), False otherwise.
        """

        with self._lock:
        try:
            with timed_lock(self._lock, "Atik"):
                if self._cam.is_connected():
                    return True
                try:
@@ -107,6 +109,12 @@ class Camera(BaseDevice):
                    return False

                return True
        except TimeoutError as e:
            msg = str(e)
            if msg not in self.error:
                self.error.append(msg)
                log.error(msg)
            return False

    def __del__(self):
        """Safely disconnect from the camera."""
@@ -126,7 +134,7 @@ class Camera(BaseDevice):
        if not self._check_connection():
            return None
        try:
            with self._lock:
            with timed_lock(self._lock, "Atik"):
                return getattr(self._cam, method)(*args)
        except Exception as e:
            msg = f"Atik get({method}) failed: {e}"
@@ -141,7 +149,7 @@ class Camera(BaseDevice):
        if not self._check_connection():
            return None
        try:
            with self._lock:
            with timed_lock(self._lock, "Atik"):
                return getattr(self._cam, method)(*args)
        except Exception as e:
            msg = f"Atik put({method}) failed: {e}"
@@ -158,7 +166,7 @@ class Camera(BaseDevice):
        if not self._check_connection():
            return None
        try:
            with self._lock:
            with timed_lock(self._lock, "Atik"):
                return self._get_image()
        except Exception as e:
            msg = f"Atik get(get_image) failed: {e}"
@@ -282,7 +290,7 @@ class Camera(BaseDevice):
                f"Cannot start exposure, camera is not idle. State: {self.state}")
            self.error.append("Camera not idle")
            return
        with self._lock:
        with timed_lock(self._lock, "Atik"):
            self._last_exptime   = duration
            self._last_imagetype = imagetype
            self._last_datetime  = datetime
@@ -339,7 +347,7 @@ class Camera(BaseDevice):
        if not self._check_connection():
            return None

        with self._lock:
        with timed_lock(self._lock, "Atik"):
            last_exptime = self._last_exptime
            last_imagetype = self._last_imagetype
            last_datetime = self._last_datetime
@@ -394,7 +402,7 @@ class Camera(BaseDevice):
        hdr['SET-TEMP'] = (self._setpoint if self._setpoint is not None else 0.0,
                           "[C] CCD setpoint temperature")

        with self._lock:
        with timed_lock(self._lock, "Atik"):
            subframe = self._subframe

        hdr['XPIXSZ'] = (self._props.get('PixelMicronsX', 0), "[um] Pixel X size")
@@ -519,7 +527,7 @@ class Camera(BaseDevice):
        self.put('set_subframe', x, y, w, h)
        if len(self.error) > errors_before:
            return False
        with self._lock:
        with timed_lock(self._lock, "Atik"):
            self._subframe = [x, y, w, h]

        return True
@@ -699,7 +707,7 @@ class Camera(BaseDevice):
    def xystart(self):
        """list of int : Sub-frame [X, Y] origin in binned pixels."""

        with self._lock:
        with timed_lock(self._lock, "Atik"):
            x, y, w, h = self._subframe
        bx, by = self.binning
        bx = bx or 1
@@ -711,7 +719,7 @@ class Camera(BaseDevice):
    def xyend(self):
        """list of int : Sub-frame [X, Y] end corner in binned pixels."""

        with self._lock:
        with timed_lock(self._lock, "Atik"):
            x, y, w, h = self._subframe
        bx, by = self.binning
        bx = bx or 1
@@ -768,7 +776,7 @@ class Camera(BaseDevice):
    def xrange(self):
        """list of int : Sub-frame X extent [start, end] in binned pixels."""

        with self._lock:
        with timed_lock(self._lock, "Atik"):
            x, y, w, h = self._subframe
        bx, by = self.binning
        bx = bx or 1
@@ -779,7 +787,7 @@ class Camera(BaseDevice):
    def yrange(self):
        """list of int : Sub-frame Y extent [start, end] in binned pixels."""

        with self._lock:
        with timed_lock(self._lock, "Atik"):
            x, y, w, h = self._subframe
        bx, by = self.binning
        by = by or 1
@@ -811,7 +819,7 @@ class Camera(BaseDevice):
        fan_power = round((level / 255.0) * 100) if maxlvl > 0 else 0

        b_x, b_y = self.get('get_binning') or (1, 1)
        with self._lock:
        with timed_lock(self._lock, "Atik"):
            x_sf, y_sf, w_sf, h_sf = self._subframe
        nx, ny = self._props.get('nPixelsX', 0), self._props.get('nPixelsY', 0)
        x_start = x_sf // b_x if b_x else 0
+41 −27
Original line number Diff line number Diff line
@@ -79,7 +79,15 @@ class Mako(BaseDevice):
        """

        if self._cam is None:
            with Mako._connect_lock:
            try:
                # Bounded: vmb.__enter__()/get_camera_by_id()/cam.__enter__()
                # below are direct blocking SDK calls with no timeout of
                # their own — if the lock itself is free but one of them
                # wedges, this still waits forever without the acquire
                # timeout. That part isn't fixable from here (see
                # timed_lock's docstring); this only bounds how long other
                # threads queue up behind a connect that's already stuck.
                with check.timed_lock(Mako._connect_lock, "Mako connect"):
                    if self._cam is None:  # re-check: another thread may have connected while we waited
                        self.vmb.__enter__()
                        try:
@@ -108,6 +116,12 @@ class Mako(BaseDevice):
                                self.error.append(msg)
                            log.error(msg)
                            raise ConnectionError(msg)
            except TimeoutError as e:
                msg = str(e)
                if msg not in self.error:
                    self.error.append(msg)
                log.error(msg)
                raise ConnectionError(msg)

        return self._cam

+1 −1
Original line number Diff line number Diff line
@@ -685,7 +685,7 @@ class Camera(STL):
        b = self._binning
        left, top = start_x // b, start_y // b
        width, height = sub_width // b, sub_height // b
        with self._lock:
        with check.timed_lock(self._lock, "STL"):
            self._last_matrix = self._read_frame(left, top, width, height)

        return self._last_matrix
+27 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ specific types of functions.

# System modules
import socket
from contextlib import contextmanager

# Third-party modules
import requests
@@ -16,6 +17,32 @@ from pyvantagepro.device import NoDeviceException
# Other templates
from ..utils.logger import log

LOCK_TIMEOUT = 5.0


@contextmanager
def timed_lock(lock, name, timeout=LOCK_TIMEOUT):
    '''Acquire *lock* with a bound instead of blocking forever.

    Several device SDKs (Atik, SBIG/STL, Mako) are wrapped in a lock but
    make blocking native calls with no timeout of their own — if one call
    ever wedges inside the SDK, an unbounded ``with lock:`` leaves every
    other thread waiting on it (including telemetry polling) hung
    forever too, with no error anywhere. This raises TimeoutError instead,
    so the caller degrades to a visible error on that one device instead
    of silently freezing everything that touches it. It does not free the
    thread stuck inside the original call — that one is still lost — but
    it stops the hang from cascading to every other caller.
    '''

    if not lock.acquire(timeout=timeout):
        raise TimeoutError(
            f"{name}: lock busy for >{timeout}s — a thread is likely stuck inside the SDK")
    try:
        yield
    finally:
        lock.release()

# class ManualRetry(Exception):
#     '''
#     Manually raise an exception, so it is possible to handle a retry