Files
AareDAQ/tests/unit/gui/test_rotation_speed_limits.py
leonarski_fandClaude Opus 5 8833f3a9d1 fix: keep user values in the scan panels across sample mounts
The panels kept the effective detector distance, resolution and
transmission in their own attributes, and a sample change overwrote those
from the spreadsheet whatever the Database/User toggle said. The fields
went on showing the user's numbers while the scan request carried the
database ones, so a value had to be re-entered after every mount.

The fields are now the only place a value lives. Each setting is worth,
in order: what the user typed while "User values" is selected, what the
mounted sample asks for, the panel default - one rule, ScanSettingsPanel
._setting, that each panel spells out a line at a time. Overrides are per
setting, so a setting the user did not touch keeps following the sample.

Along the way:

* DbOverrideLineEdit is gone; the panels use NumberLineEdit directly, and
  the per-widget database/user bookkeeping is replaced by SampleParameters,
  the one place that knows the aareDB spreadsheet column names.
* The (name, widget, converter) mapping loops with getattr/hasattr are
  replaced by explicit per-field commit slots.
* The omega speed cap no longer drives one field's validator from another
  field's value: it is a read-out plus a check on the pair at Run time.
* _add_row/_add_pair_row build the label/field/unit rows, which also puts
  the Total angle degree sign on its own row instead of the header above.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JHoUkj66jxByS2ypY5h9Mn
2026-09-08 11:56:57 +02:00

104 lines
3.4 KiB
Python

"""Omega speed cap (500 deg/s) and per-beamline min image time on the rotation panel.
Why: the image angle and the image time are each fine on their own - it is the
pair that can ask the goniometer to turn too fast. So the cap is a read-out
plus a gate on the Run button, and only the per-beamline minimum image time is
a limit of the field itself.
"""
import pytest
from aarecommon.math.diffraction_geometry import DiffractionGeometry
from aarecommon.models.models import BeamlineStateEnum
from PySide6.QtWidgets import QMessageBox
from aare.gui.panels.rotation_data_collection import RotationDataCollectionPanel
def _panel(monkeypatch, beamline):
# mx_beamline() reads the env on every call, so set it before construction
monkeypatch.setenv("BEAMLINE", beamline)
diffraction = DiffractionGeometry(
energy_keV=12.0,
dtz_mm=150.0,
pixel_size_mm=0.075,
beam_center_pxl=(1000.0, 1000.0),
detector_size_pxl=(2000, 2000),
detector_description="Eiger 16M",
detector_serial_number="123",
poni_rot1_rad=0.0,
poni_rot2_rad=0.0,
)
return RotationDataCollectionPanel(diffraction=diffraction)
def _runnable(panel, monkeypatch):
panel._beamline_state = BeamlineStateEnum.SampleAlignment
monkeypatch.setattr(panel, "check_before_run", lambda scan_kind: True)
return panel
def _commit(field, text):
field.setText(text)
field.on_editing_finished()
def _accepts(field, text) -> bool:
field.setText(text)
return field.hasAcceptableInput()
@pytest.mark.parametrize(
"beamline,too_short,ok", [("X06DA", "0.0010", "0.0012"), ("X10SA", "0.0050", "0.0090")]
)
def test_min_image_time_per_beamline(qapp, monkeypatch, beamline, too_short, ok):
panel = _panel(monkeypatch, beamline)
for field in (panel.image_time_enter, panel.screening_image_time_enter):
assert not _accepts(field, too_short)
assert _accepts(field, ok)
def test_speed_readout_follows_the_pair(qapp, monkeypatch):
panel = _panel(monkeypatch, "X06DA")
_commit(panel.image_angle, "1.000")
_commit(panel.image_time_enter, "0.1000")
assert panel.omega_speed.text() == "10.0"
assert not panel.omega_speed.styleSheet() # within the cap
_commit(panel.image_time_enter, "0.0012") # 833 deg/s
assert panel.omega_speed.text() == "833.3"
assert panel.omega_speed.styleSheet() # marked red
def test_run_blocked_above_the_speed_cap(qapp, monkeypatch):
panel = _runnable(_panel(monkeypatch, "X06DA"), monkeypatch)
boxes = []
monkeypatch.setattr(QMessageBox, "critical", lambda *a, **k: boxes.append(a))
emitted = []
panel.rotation_scan.connect(emitted.append)
_commit(panel.image_angle, "1.000")
_commit(panel.image_time_enter, "0.0012") # 833 deg/s, over the cap
panel.run_measurement()
assert emitted == []
assert len(boxes) == 1
_commit(panel.image_time_enter, "0.0100") # 100 deg/s
panel.run_measurement()
assert len(emitted) == 1
def test_run_blocked_while_field_red(qapp, monkeypatch):
panel = _panel(monkeypatch, "X06DA")
boxes = []
monkeypatch.setattr(QMessageBox, "critical", lambda *a, **k: boxes.append(a))
emitted = []
panel.rotation_scan.connect(emitted.append)
panel._beamline_state = BeamlineStateEnum.SampleAlignment
panel.image_time_enter.setText("0.0010") # below 1/900 s, stays red
panel.run_measurement()
assert emitted == []
assert len(boxes) == 1