daq: send commanded dtz (not live readback) to JFJoch/DB + wait for arrival
The detector_distance_mm sent to JFJoch and written to the DB came from the live detector readback (diffraction.dtz_mm) captured before the detector finished moving, so a scan requested at e.g. 100 mm could be recorded at the old position (e.g. 800 mm). - JFJoch + DB now read the validated request target (request.dtz) instead of the live diffraction.dtz_mm; diffraction.dtz_mm stays live for the local-contact readout. - jfjoch: raise if request.dtz is unset (setup_datacollection must validate it first) instead of silently falling back to the live readback. - raster + rotation services: wait for the detector to reach request.dtz (set_dtz wait=True) right before triggering the scan, since the sa2dc move is non-blocking. (Raster setup-before-measure reorder landed earlier in daq.py.) - tests: request.dtz now required in jfjoch/aaredb tests; xtal-snapshot exposure assertion updated to 1 ms. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
f7b5008be5
commit
ec45bf3729
7 files changed
+33
-15
No files matched your search
@@ -202,7 +202,7 @@ class AareWrapper:
|
||||
model=d.diffraction.detector_description,
|
||||
type="photon-counting",
|
||||
serialNumber=d.diffraction.detector_serial_number,
|
||||
detectorDistance_mm=d.diffraction.dtz_mm,
|
||||
detectorDistance_mm=r.dtz,
|
||||
resolution_at_edge_Ang=d.diffraction.max_resolution_angstrom,
|
||||
beamCenterX_px=d.diffraction.beam_center_pxl[0],
|
||||
beamCenterY_px=d.diffraction.beam_center_pxl[1],
|
||||
@@ -268,7 +268,7 @@ class AareWrapper:
|
||||
model=d.diffraction.detector_description,
|
||||
type="photon-counting",
|
||||
serialNumber=d.diffraction.detector_serial_number,
|
||||
detectorDistance_mm=round(d.diffraction.dtz_mm,3),
|
||||
detectorDistance_mm=round(r.dtz,3),
|
||||
resolution_at_edge_Ang=round(d.diffraction.max_resolution_angstrom,3),
|
||||
beamCenterX_px=round(d.diffraction.beam_center_pxl[0],2),
|
||||
beamCenterY_px=round(d.diffraction.beam_center_pxl[1],2),
|
||||
|
||||
@@ -317,6 +317,11 @@ class RasterService:
|
||||
self.logger.debug(
|
||||
f"Starting grid scan with {request.n_x}x{request.n_y} points, exp time {request.exp_time_s}s"
|
||||
)
|
||||
if not self.ctx.deps.cfg.simulated_detector:
|
||||
# The detector move (sa2dc) is non-blocking; ensure it has reached
|
||||
# the requested distance before triggering the scan, so data is
|
||||
# collected at the dtz already reported to JFJoch/DB.
|
||||
self.ctx.deps.devs.set_dtz(request.dtz, wait=True)
|
||||
self.ctx.deps.devs.aerotech.grid_scan(
|
||||
grid_elem_size_y_um=request.grid_size_mm.y * 1000,
|
||||
grid_elem_size_x_um=request.grid_size_mm.x * 1000,
|
||||
|
||||
@@ -41,6 +41,10 @@ class RotationService:
|
||||
|
||||
if not self.ctx.deps.cfg.simulated_detector:
|
||||
self.ctx.deps.jfjoch.wait_till_running(timeout=60.0)
|
||||
# The detector move (sa2dc) is non-blocking; ensure it has reached the
|
||||
# requested distance before triggering the scan, so data is collected
|
||||
# at the dtz already reported to JFJoch/DB.
|
||||
self.ctx.deps.devs.set_dtz(request.dtz, wait=True)
|
||||
|
||||
if request.screening:
|
||||
self.ctx.deps.devs.aerotech.screening_scan(
|
||||
@@ -84,8 +88,11 @@ class RotationService:
|
||||
def run(self, request: RotationScanRequest) -> CompletedRotationScan:
|
||||
sample = self.ctx.sample
|
||||
|
||||
self._prepare_detector(request)
|
||||
# Validate + set the requested dtz (cfg.dtz, requires request.dtz) BEFORE
|
||||
# configuring JFJoch, so JFJoch receives the commanded target rather than
|
||||
# the live, still-in-transit detector position.
|
||||
self.ctx.services.datacollection.prepare(request)
|
||||
self._prepare_detector(request)
|
||||
|
||||
if sample is not None and sample.db_id is not None and self.ctx.services.events is not None:
|
||||
self.ctx.services.events.send(sample.db_id, SampleEventType.COLLECTING)
|
||||
|
||||
@@ -125,14 +125,18 @@ class JFJochWrapper:
|
||||
wedge = None
|
||||
images = r.steps
|
||||
trigger = 1
|
||||
|
||||
if r.dtz is None:
|
||||
raise ValueError(
|
||||
"Scan request has no detector distance (dtz); setup_datacollection "
|
||||
"must validate and set request.dtz before JFJoch is configured"
|
||||
)
|
||||
#build common dataset settings
|
||||
dataset_settings = jfjoch_client.DatasetSettings(
|
||||
beam_x_pxl=s.diffraction.beam_center_pxl[0],
|
||||
beam_y_pxl=s.diffraction.beam_center_pxl[1],
|
||||
ntrigger= trigger,
|
||||
images_per_trigger=images,
|
||||
detector_distance_mm=s.diffraction.dtz_mm,
|
||||
detector_distance_mm=r.dtz,
|
||||
file_prefix=f"{data_folder}/{r.file_prefix}",
|
||||
incident_energy_keV=s.diffraction.energy_keV,
|
||||
sample_name=sample,
|
||||
|
||||
@@ -157,7 +157,7 @@ def test_upload_jpg(mock_post, mock_api, mock_bl):
|
||||
def test_create_rotation_run(mock_sample, mock_api, mock_bl, sample_info, daq_status):
|
||||
wrapper = AareWrapper(bl=mock_bl)
|
||||
# Standard rotation
|
||||
req = RotationScanRequest(exp_time_s=0.1, incr_omega_deg=0.1, steps=100, file_prefix="test_prefix")
|
||||
req = RotationScanRequest(exp_time_s=0.1, incr_omega_deg=0.1, steps=100, file_prefix="test_prefix", dtz=200.0)
|
||||
wrapper.create_rotation_run(sample_info, req, daq_status)
|
||||
mock_sample.return_value.create_experiment_parameters_for_sample.assert_called_once()
|
||||
|
||||
@@ -170,7 +170,7 @@ def test_create_rotation_run(mock_sample, mock_api, mock_bl, sample_info, daq_st
|
||||
@patch("aareDB.SamplesRunnerApi")
|
||||
def test_create_rotation_run_screening(mock_sample, mock_api, mock_bl, sample_info, daq_status):
|
||||
wrapper = AareWrapper(bl=mock_bl)
|
||||
req = RotationScanRequest(exp_time_s=0.1, incr_omega_deg=0.1, steps=100, file_prefix="test_prefix", screening=True, wedge_omega_deg=5.0)
|
||||
req = RotationScanRequest(exp_time_s=0.1, incr_omega_deg=0.1, steps=100, file_prefix="test_prefix", screening=True, wedge_omega_deg=5.0, dtz=200.0)
|
||||
wrapper.create_rotation_run(sample_info, req, daq_status)
|
||||
mock_sample.return_value.create_experiment_parameters_for_sample.assert_called_once()
|
||||
|
||||
@@ -178,7 +178,7 @@ def test_create_rotation_run_screening(mock_sample, mock_api, mock_bl, sample_in
|
||||
@patch("aareDB.SamplesRunnerApi")
|
||||
def test_create_rotation_run_error(mock_sample, mock_api, mock_bl, sample_info, daq_status, caplog):
|
||||
wrapper = AareWrapper(bl=mock_bl)
|
||||
req = RotationScanRequest(exp_time_s=0.1, incr_omega_deg=0.1, steps=100, file_prefix="test_prefix")
|
||||
req = RotationScanRequest(exp_time_s=0.1, incr_omega_deg=0.1, steps=100, file_prefix="test_prefix", dtz=200.0)
|
||||
mock_sample.return_value.create_experiment_parameters_for_sample.side_effect = Exception("Rotation error")
|
||||
wrapper.create_rotation_run(sample_info, req, daq_status)
|
||||
assert "Rotation error" in caplog.text
|
||||
@@ -187,7 +187,7 @@ def test_create_rotation_run_error(mock_sample, mock_api, mock_bl, sample_info,
|
||||
@patch("aareDB.SamplesRunnerApi")
|
||||
def test_create_gridscan_run(mock_sample, mock_api, mock_bl, sample_info, daq_status):
|
||||
wrapper = AareWrapper(bl=mock_bl)
|
||||
req = RasterGridRequest(exp_time_s=0.1, n_x=10, n_y=10, grid_size_mm=Coordinate(x=0.01, y=0.01), smargon_top_left=None, file_prefix="test_grid_prefix")
|
||||
req = RasterGridRequest(exp_time_s=0.1, n_x=10, n_y=10, grid_size_mm=Coordinate(x=0.01, y=0.01), smargon_top_left=None, file_prefix="test_grid_prefix", dtz=200.0)
|
||||
wrapper.create_gridscan_run(sample_info, req, daq_status)
|
||||
mock_sample.return_value.create_experiment_parameters_for_sample.assert_called_once()
|
||||
|
||||
@@ -200,7 +200,7 @@ def test_create_gridscan_run(mock_sample, mock_api, mock_bl, sample_info, daq_st
|
||||
@patch("aareDB.SamplesRunnerApi")
|
||||
def test_create_gridscan_run_error(mock_sample, mock_api, mock_bl, sample_info, daq_status, caplog):
|
||||
wrapper = AareWrapper(bl=mock_bl)
|
||||
req = RasterGridRequest(exp_time_s=0.1, n_x=10, n_y=10, grid_size_mm=Coordinate(x=0.01, y=0.01), smargon_top_left=None, file_prefix="test_grid_prefix")
|
||||
req = RasterGridRequest(exp_time_s=0.1, n_x=10, n_y=10, grid_size_mm=Coordinate(x=0.01, y=0.01), smargon_top_left=None, file_prefix="test_grid_prefix", dtz=200.0)
|
||||
mock_sample.return_value.create_experiment_parameters_for_sample.side_effect = Exception("Grid error")
|
||||
wrapper.create_gridscan_run(sample_info, req, daq_status)
|
||||
assert "Grid error" in caplog.text
|
||||
|
||||
@@ -118,7 +118,7 @@ def test_sa2xtal_snapshot(mock_devs, mock_cfg):
|
||||
mock_devs.bec_worker.move_to = MagicMock()
|
||||
|
||||
sa2xtal_snapshot(mock_devs, mock_cfg)
|
||||
assert mock_devs.samcam_settings.exposure == 0.0005
|
||||
assert mock_devs.samcam_settings.exposure == 0.001
|
||||
_assert_bec_moved(mock_devs, BeamlineState.XTAL_SNAPSHOT)
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ def test_dc2xtal_snapshot(mock_devs, mock_cfg):
|
||||
mock_devs.bec_worker.move_to = MagicMock()
|
||||
|
||||
dc2xtal_snapshot(mock_devs, mock_cfg)
|
||||
assert mock_devs.samcam_settings.exposure == 0.0005
|
||||
assert mock_devs.samcam_settings.exposure == 0.001
|
||||
_assert_bec_moved(mock_devs, BeamlineState.XTAL_SNAPSHOT)
|
||||
|
||||
|
||||
|
||||
@@ -121,7 +121,8 @@ def test_measure_rotation(jfjoch_wrapper, mock_jfjoch_client):
|
||||
incr_omega_deg=0.1,
|
||||
steps=100,
|
||||
exp_time_s=0.01,
|
||||
file_prefix="test_prefix"
|
||||
file_prefix="test_prefix",
|
||||
dtz=200.0,
|
||||
)
|
||||
s = create_mock_daq_status()
|
||||
s.sample = SampleShortInfo(sample_name="S1", user="p12345", db_id=1, dewar_name="D1", puck_name="P1", run_number=1, pin=1)
|
||||
@@ -132,7 +133,7 @@ def test_measure_rotation(jfjoch_wrapper, mock_jfjoch_client):
|
||||
|
||||
def test_measure_rotation_error(jfjoch_wrapper, mock_jfjoch_client):
|
||||
mock_jfjoch_client.start_post.side_effect = Exception("API Error")
|
||||
r = RotationScanRequest(exp_time_s=0.01, incr_omega_deg=0.1, steps=10)
|
||||
r = RotationScanRequest(exp_time_s=0.01, incr_omega_deg=0.1, steps=10, dtz=200.0)
|
||||
s = create_mock_daq_status()
|
||||
s.sample = SampleShortInfo(sample_name="S1", user="p12345", db_id=1, dewar_name="D1", puck_name="P1", run_number=1, pin=1)
|
||||
|
||||
@@ -147,7 +148,8 @@ def test_measure_raster(jfjoch_wrapper, mock_jfjoch_client):
|
||||
grid_size_mm=Coordinate(x=0.01, y=0.01),
|
||||
exp_time_s=0.01,
|
||||
file_prefix="raster_prefix",
|
||||
smargon_top_left=SmargonCoordinate(sh_mm=Coordinate(x=0, y=0))
|
||||
smargon_top_left=SmargonCoordinate(sh_mm=Coordinate(x=0, y=0)),
|
||||
dtz=200.0,
|
||||
)
|
||||
s = create_mock_daq_status()
|
||||
s.sample = SampleShortInfo(sample_name="S1", user="p12345", db_id=1, dewar_name="D1", puck_name="P1", run_number=1, pin=1)
|
||||
|
||||
Reference in new issue
Block a user