fix(LamNI): push smear composite via new IDSCamera RPC method

dev.cam_xeye.image.put(composite) failed on real hardware --
PreviewSignal is a BECMessageSignal subclass, and BEC hardcodes
rpc_access=False for that signal_info, so the client-side device proxy
never exposes `image` as an attribute at all (confirmed: this is a
structural BEC constraint, not specific to this device -- no existing
RPC surface, device or widget, offers a raw-array setter either).

Add IDSCamera.push_preview_image(data), a small device-server-side
method (alongside the existing get_last_image()) that does
self.image.put(data) from within the device-server process, where
PreviewSignal is a normal ophyd attribute. _push_smear_composite now
calls dev.cam_xeye.push_preview_image(composite) instead of writing to
the signal directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
x01dc
2026-07-24 16:08:01 +02:00
co-authored by Claude Sonnet 5
parent 71b9563abb
commit 6d65a87ae4
3 changed files with 30 additions and 3 deletions
@@ -674,9 +674,15 @@ class XrayEyeAlign:
between this push and the next raw frame the live thread would
otherwise publish to the same PreviewSignal/device_preview
channel the GUI is subscribed to.
dev.cam_xeye.image is a PreviewSignal, which is not remotely
writable from client code (BEC excludes BECMessageSignal
subclasses from the client-side device RPC proxy) -- push via the
push_preview_image() device method instead, which runs
device-server side where self.image is a normal ophyd attribute.
"""
dev.cam_xeye.live_mode_enabled.put(False)
dev.cam_xeye.image.put(composite)
dev.cam_xeye.push_preview_image(composite)
time.sleep(hold_display_s)
dev.cam_xeye.live_mode_enabled.put(True)
+22 -1
View File
@@ -51,7 +51,14 @@ class IDSCamera(PSIDeviceBase):
kind=Kind.config,
)
USER_ACCESS = ["start_live_mode", "stop_live_mode", "mask", "set_rect_roi", "get_last_image"]
USER_ACCESS = [
"start_live_mode",
"stop_live_mode",
"mask",
"set_rect_roi",
"get_last_image",
"push_preview_image",
]
def __init__(
self,
@@ -195,6 +202,20 @@ class IDSCamera(PSIDeviceBase):
if image:
return image.data
def push_preview_image(self, data: np.ndarray) -> None:
"""Push an arbitrary array to this camera's own preview signal.
self.image is a PreviewSignal, which is deliberately excluded from
the client-side device RPC/signal proxy (rpc_access defaults to
False for BECMessageSignal subclasses, with no override) -- so
client code cannot do dev.cam_xeye.image.put(...) directly. This
method runs device-server side, where self.image is a normal ophyd
attribute, and is the supported way for a client to publish a
custom (e.g. composite/processed) image through the same
device_preview channel the GUI already subscribes to.
"""
self.image.put(data)
############## User Interface Methods ##############
def on_connected(self):
@@ -395,7 +395,7 @@ def test_push_smear_composite_duty_cycles_live_mode(bec_client_mock):
False,
True,
]
dev_mock.cam_xeye.image.put.assert_called_once_with(composite)
dev_mock.cam_xeye.push_preview_image.assert_called_once_with(composite)
def test_smear_sweep_max_projection(bec_client_mock):