fix(eiger): resolve live preview data channel per detector, not "default" only
The Eiger2 Si 500K's multi-threshold JFJ preview messages key the frame under a threshold name (e.g. "threshold_1"), not "default" as eiger_1_5/ eiger_9 do -- confirmed by capturing the live JFJ ZMQ preview stream. _preview_callback hardcoded "default", so it silently dropped every frame for eiger2_si_500k (251+ "without data" errors in DeviceServer.log, matching the empty BEC image widget). Now prefers "default" when present and falls back to whichever single channel the broker actually sent. Also document the JFJoch Web UI (http://sls-jfjoch-001.psi.ch:8080) and the Eiger 2 Si 500K detector server address (129.129.122.159) in bl_detectors.yaml's Eiger section. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c88256b9f6
commit
addffa1299
3 files changed
+58
-21
No files matched your search
@@ -1,23 +1,16 @@
|
||||
eiger2_si_500k:
|
||||
description: Eiger 2 Si 500K detector
|
||||
deviceClass: csaxs_bec.devices.jungfraujoch.eiger2_si_500k.Eiger2Si500K
|
||||
deviceConfig:
|
||||
detector_distance: 1000
|
||||
beam_center: [200, 200]
|
||||
onFailure: raise
|
||||
enabled: True
|
||||
readoutPriority: async
|
||||
softwareTrigger: False
|
||||
deviceTags:
|
||||
- bl_detectors
|
||||
- eiger
|
||||
# Eiger detectors (JungfrauJoch). Only one may be enabled at a time -- the JFJ broker
|
||||
# serves a single selected/initialised detector, so switching the active one here also
|
||||
# requires selecting/initialising the matching detector on the JFJ broker itself.
|
||||
# JungfrauJoch-side config (detector selection/init, pixel mask, azimuthal integration
|
||||
# settings) is done in the Web UI at http://sls-jfjoch-001.psi.ch:8080.
|
||||
# The Eiger 2 Si 500K detector server itself is reachable at 129.129.122.159.
|
||||
# eiger2_si_500k:
|
||||
|
||||
# eiger_1_5:
|
||||
# description: Eiger 1.5M in-vacuum detector
|
||||
# deviceClass: csaxs_bec.devices.jungfraujoch.eiger_1_5m.Eiger1_5M
|
||||
# description: Eiger 2 Si 500K detector
|
||||
# deviceClass: csaxs_bec.devices.jungfraujoch.eiger2_si_500k.Eiger2Si500K
|
||||
# deviceConfig:
|
||||
# detector_distance: 2200
|
||||
# beam_center: [870, 1203]
|
||||
# detector_distance: 1000
|
||||
# beam_center: [200, 200]
|
||||
# onFailure: raise
|
||||
# enabled: True
|
||||
# readoutPriority: async
|
||||
@@ -26,6 +19,20 @@ eiger2_si_500k:
|
||||
# - bl_detectors
|
||||
# - eiger
|
||||
|
||||
eiger_1_5:
|
||||
description: Eiger 1.5M in-vacuum detector
|
||||
deviceClass: csaxs_bec.devices.jungfraujoch.eiger_1_5m.Eiger1_5M
|
||||
deviceConfig:
|
||||
detector_distance: 2200
|
||||
beam_center: [870, 1203]
|
||||
onFailure: raise
|
||||
enabled: True
|
||||
readoutPriority: async
|
||||
softwareTrigger: False
|
||||
deviceTags:
|
||||
- bl_detectors
|
||||
- eiger
|
||||
|
||||
# eiger_9:
|
||||
# description: Eiger 9M detector
|
||||
# deviceClass: csaxs_bec.devices.jungfraujoch.eiger_9m.Eiger9M
|
||||
|
||||
@@ -226,11 +226,18 @@ class Eiger(PSIDeviceBase):
|
||||
message (dict): The message received from the preview stream.
|
||||
"""
|
||||
if message.get("type", "") == "image":
|
||||
data = message.get("data", {}).get("default", None)
|
||||
if data is None:
|
||||
channels = message.get("data", {})
|
||||
# The data dict's key varies by detector: "default" for eiger_1_5/eiger_9,
|
||||
# but a threshold name (e.g. "threshold_1") for the multi-threshold-capable
|
||||
# Eiger2 Si 500K. Prefer "default" when present, else take whichever single
|
||||
# channel the broker actually sent.
|
||||
frame = channels.get("default")
|
||||
if frame is None:
|
||||
frame = next(iter(channels.values()), None)
|
||||
if frame is None:
|
||||
logger.error(f"Received image message on device {self.name} without data.")
|
||||
return
|
||||
self.preview_image.put(data)
|
||||
self.preview_image.put(frame)
|
||||
|
||||
# pylint: disable=missing-function-docstring
|
||||
@property
|
||||
|
||||
@@ -542,6 +542,29 @@ def test_eiger_preview_callback(eiger_1_5m):
|
||||
mock_preview_put.assert_not_called()
|
||||
|
||||
|
||||
def test_eiger_preview_callback_falls_back_to_non_default_channel(eiger2_si_500k):
|
||||
"""The Eiger2 Si 500K's multi-threshold preview data has no "default" key (it's
|
||||
named after the threshold, e.g. "threshold_1" -- confirmed against a live capture
|
||||
of the JFJ preview stream); the callback must still forward that single channel.
|
||||
"""
|
||||
eiger = eiger2_si_500k
|
||||
frame = np.array([[1, 2], [3, 4]])
|
||||
msg = {"type": "image", "data": {"threshold_1": frame}}
|
||||
with mock.patch.object(eiger.preview_image, "put") as mock_preview_put:
|
||||
eiger._preview_callback(msg)
|
||||
mock_preview_put.assert_called_once_with(frame)
|
||||
|
||||
|
||||
def test_eiger_preview_callback_logs_when_data_is_truly_empty(eiger_1_5m):
|
||||
"""No channel at all (empty or missing data dict) must still be treated as missing,
|
||||
not silently pass through some other unrelated dict key."""
|
||||
eiger = eiger_1_5m
|
||||
for msg in [{"type": "image", "data": {}}, {"type": "image"}]:
|
||||
with mock.patch.object(eiger.preview_image, "put") as mock_preview_put:
|
||||
eiger._preview_callback(msg)
|
||||
mock_preview_put.assert_not_called()
|
||||
|
||||
|
||||
def test_eiger_acquisition_matches_the_gated_window(eiger_1_5m):
|
||||
"""The detector must acquire for exp_time - readout, not for the whole period.
|
||||
|
||||
|
||||
Reference in new issue
Block a user