fix(vision): declare device_manager explicitly in SampleVisionMonitor

The device server's construct_device_obj() only injects device_manager into
a device's constructor when that class's own __init__ signature names it
explicitly (inspect.signature() doesn't look through **kwargs into a base
class). SampleVisionMonitor's __init__ only accepted **kwargs, so
self.device_manager was always None once instantiated by the real device
server -- resolve_vision_interface() then failed at call time with "device
manager is not available", even though unit tests passed (they instantiate
the class directly with device_manager=mock as a kwarg, which works either
way). Declares device_manager as an explicit keyword and forwards it,
matching the working ddg_1.py pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C9zgK28uPoZWJFkJWV4P1G
This commit is contained in:
Mirko HollerandClaude Sonnet 5 authored and Mirko Holler committed 2026-10-26 14:15:45 +01:00
1 parent 9b65b2ddac
commit 0c6ab2e06e
1 file changed
+9 -2
@@ -21,7 +21,7 @@ from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase
from csaxs_bec.devices.vision import vision_toolkit as vt
if TYPE_CHECKING:
from bec_lib.devicemanager import ScanInfo
from bec_lib.devicemanager import DeviceManagerBase, ScanInfo
class SampleVisionMonitorError(Exception):
@@ -56,6 +56,7 @@ class SampleVisionMonitor(PSIDeviceBase):
position_tolerance_px: float | None = None,
prefix: str = "",
scan_info: "ScanInfo | None" = None,
device_manager: "DeviceManagerBase | None" = None,
**kwargs,
):
"""Initialize the sample vision monitor.
@@ -65,6 +66,10 @@ class SampleVisionMonitor(PSIDeviceBase):
vision_interface_name (str): Name of the ``VisionInterface`` device this monitor scores
against, resolved through the device manager at call time (same lazy-lookup pattern
as ``VisionInterface``'s own camera dependency).
device_manager (DeviceManagerBase | None): Device manager, needed to resolve
``vision_interface_name`` at call time -- must be declared explicitly here (not
just inherited via ``**kwargs``) since the device server only injects it into
classes whose own ``__init__`` signature names it.
warp_mode (str | int | None): Which drift between a reference and the current frame
``score()``'s *shape* score should tolerate (register away before comparing
contours) rather than count toward the score -- one of ``vision_toolkit.WARP_MODES``
@@ -83,7 +88,9 @@ class SampleVisionMonitor(PSIDeviceBase):
at all; a pin/gripper in a different spot is not, by itself, treated as a problem.
Runtime-adjustable via :meth:`set_position_tolerance_px`.
"""
super().__init__(name=name, prefix=prefix, scan_info=scan_info, **kwargs)
super().__init__(
name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs
)
self._vision_interface_name = vision_interface_name
self._warp_mode = warp_mode
self._position_tolerance_px = position_tolerance_px