fix(lamni): tighten circfov/piezo-range GUI limits, enforce ranges in CLI
CI for csaxs_bec / test (push) Successful in 2m18s
CI for csaxs_bec / test (push) Successful in 2m18s
- tomo_circfov: GUI upper bound 200.0 -> 500.0 um (floor kept at 0.0 so "0 = disabled" -- checked explicitly in lamni_fermat_scan.py:484 -- still works) - lamni_piezo_range_x/y: GUI upper bound 200.0 -> 80.0 um, matching the "(max 80)" already printed in the CLI prompt. The property setter's large_range_scan bypass is left untouched (uncertain hardware rationale) -- this only tightens the GUI/CLI entry points, which is where "<80" actually becomes enforced in practice, since both now validate before ever calling the (still-conditionally-permissive) property setter. - LamNI._get_val() now supports min_/max_ (mirrors Flomni._get_val() exactly, added previously) -- LamNI's tomo_parameters() CLI wizard previously had zero range validation at all. Wired up for tomo_countingtime (0.001-100.0), tomo_shellstep (0.025-20.0), lamni_piezo_range_x/y (0.0-80.0), and tomo_circfov (0.0-500.0). - tomo_stitch_overlap gets a brand new CLI prompt (0.0-10.0 um) -- it had no prompt at all before (property-only, same gap Flomni still has). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0132KBoxsovfcMNRGJhS1Pbw
This commit is contained in:
1 parent
a7e1b5ecf0
commit
d77b5cede9
3 files changed
+79
-10
No files matched your search
@@ -2153,17 +2153,26 @@ class LamNI(TomoQueueMixin, LamNIAlignmentMixin, LamNIOpticsMixin, LamniGuiTools
|
||||
print("OK. continue.")
|
||||
return
|
||||
|
||||
self.tomo_countingtime = self._get_val("<ctime> s", self.tomo_countingtime, float)
|
||||
self.tomo_shellstep = self._get_val("<step size> um", self.tomo_shellstep, float)
|
||||
self.tomo_countingtime = self._get_val(
|
||||
"<ctime> s", self.tomo_countingtime, float, min_=0.001, max_=100.0
|
||||
)
|
||||
self.tomo_shellstep = self._get_val(
|
||||
"<step size> um", self.tomo_shellstep, float, min_=0.025, max_=20.0
|
||||
)
|
||||
self.lamni_piezo_range_x = self._get_val(
|
||||
"<piezo range X (max 80)> um", self.lamni_piezo_range_x, float
|
||||
"<piezo range X (max 80)> um", self.lamni_piezo_range_x, float, min_=0.0, max_=80.0
|
||||
)
|
||||
self.lamni_piezo_range_y = self._get_val(
|
||||
"<piezo range Y (max 80)> um", self.lamni_piezo_range_y, float
|
||||
"<piezo range Y (max 80)> um", self.lamni_piezo_range_y, float, min_=0.0, max_=80.0
|
||||
)
|
||||
self.lamni_stitch_x = self._get_val("<stitch X>", self.lamni_stitch_x, int)
|
||||
self.lamni_stitch_y = self._get_val("<stitch Y>", self.lamni_stitch_y, int)
|
||||
self.tomo_circfov = self._get_val("<circular FOV> um", self.tomo_circfov, float)
|
||||
self.tomo_stitch_overlap = self._get_val(
|
||||
"<stitch overlap> um", self.tomo_stitch_overlap, float, min_=0.0, max_=10.0
|
||||
)
|
||||
self.tomo_circfov = self._get_val(
|
||||
"<circular FOV> um", self.tomo_circfov, float, min_=0.0, max_=500.0
|
||||
)
|
||||
self.ptycho_reconstruct_foldername = self._get_val(
|
||||
"Reconstruction queue", self.ptycho_reconstruct_foldername, str
|
||||
)
|
||||
@@ -2243,8 +2252,22 @@ class LamNI(TomoQueueMixin, LamNIAlignmentMixin, LamNIOpticsMixin, LamniGuiTools
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_val(msg: str, default_value, data_type):
|
||||
return data_type(input(f"{msg} ({default_value}): ") or default_value)
|
||||
def _get_val(msg: str, default_value, data_type, min_=None, max_=None):
|
||||
"""Prompt for a value, casting to data_type. When min_/max_ are
|
||||
given, re-prompts (rather than silently clamping) until the typed
|
||||
value is in range, mirroring the Tomo Parameters GUI's spinbox
|
||||
bounds (tomo_params.py) so tomo_parameters() enforces the same
|
||||
limits instead of accepting anything of the right type. Mirrors
|
||||
Flomni._get_val() exactly."""
|
||||
while True:
|
||||
val = data_type(input(f"{msg} ({default_value}): ") or default_value)
|
||||
if min_ is not None and val < min_:
|
||||
print(f"Value must be >= {min_}. Please try again.")
|
||||
continue
|
||||
if max_ is not None and val > max_:
|
||||
print(f"Value must be <= {max_}. Please try again.")
|
||||
continue
|
||||
return val
|
||||
|
||||
def _confirm_sequence_override(self, warning: str, force: bool) -> bool:
|
||||
"""Print *warning* and ask whether to proceed anyway.
|
||||
|
||||
@@ -2362,7 +2362,7 @@ SETUP_PROFILES: dict[str, dict[str, Any]] = {
|
||||
"has_180_mode": False,
|
||||
"has_single_point": False,
|
||||
"has_zero_deg_reference": True,
|
||||
"fov_fields": [("tomo_circfov", "Circular FOV (µm, 0 = disabled)", 0.0, 200.0, 2)],
|
||||
"fov_fields": [("tomo_circfov", "Circular FOV (µm, 0 = disabled)", 0.0, 500.0, 2)],
|
||||
"stitch_fields": [
|
||||
("lamni_stitch_x", "Stitch x", 0, 50),
|
||||
("lamni_stitch_y", "Stitch y", 0, 50),
|
||||
@@ -2372,8 +2372,8 @@ SETUP_PROFILES: dict[str, dict[str, Any]] = {
|
||||
("manual_shift_y", "Manual shift y (µm)", -1000.0, 1000.0, 3),
|
||||
],
|
||||
"piezo_range_fields": [
|
||||
("lamni_piezo_range_x", "Piezo range x (µm)", 0.0, 200.0, 2),
|
||||
("lamni_piezo_range_y", "Piezo range y (µm)", 0.0, 200.0, 2),
|
||||
("lamni_piezo_range_x", "Piezo range x (µm)", 0.0, 80.0, 2),
|
||||
("lamni_piezo_range_y", "Piezo range y (µm)", 0.0, 80.0, 2),
|
||||
],
|
||||
# tomo_fovx_offset/tomo_fovy_offset: alignment values (LamNIAlignmentMixin),
|
||||
# not part of _TOMO_SCAN_PARAM_NAMES -- shown/editable here (mirroring
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
"""Tests for LamNI._get_val()'s min_/max_ range validation.
|
||||
|
||||
Mirrors test_flomni_get_val_range.py -- LamNI's tomo_parameters() CLI wizard
|
||||
previously had no range validation at all (unlike the GUI's spinbox
|
||||
ranges); _get_val() now optionally re-prompts until the typed value is in
|
||||
range, same mechanism as Flomni's.
|
||||
"""
|
||||
|
||||
import builtins
|
||||
|
||||
from csaxs_bec.bec_ipython_client.plugins.LamNI.lamni import LamNI
|
||||
|
||||
|
||||
def _fake_inputs(monkeypatch, values):
|
||||
it = iter(values)
|
||||
monkeypatch.setattr(builtins, "input", lambda prompt="": next(it))
|
||||
|
||||
|
||||
def test_get_val_without_bounds_accepts_anything(monkeypatch):
|
||||
_fake_inputs(monkeypatch, ["12345"])
|
||||
assert LamNI._get_val("x", 0.0, float) == 12345.0
|
||||
|
||||
|
||||
def test_get_val_accepts_in_range_value_first_try(monkeypatch):
|
||||
_fake_inputs(monkeypatch, ["10"])
|
||||
assert LamNI._get_val("shellstep", 0.0, float, min_=0.025, max_=20.0) == 10.0
|
||||
|
||||
|
||||
def test_get_val_reprompts_when_above_max(monkeypatch, capsys):
|
||||
_fake_inputs(monkeypatch, ["999", "50"])
|
||||
result = LamNI._get_val("circfov", 0.0, float, min_=0.0, max_=500.0)
|
||||
assert result == 50.0
|
||||
assert "must be <= 500.0" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_get_val_reprompts_when_below_min(monkeypatch, capsys):
|
||||
_fake_inputs(monkeypatch, ["-5", "10"])
|
||||
result = LamNI._get_val("piezo range x", 20.0, float, min_=0.0, max_=80.0)
|
||||
assert result == 10.0
|
||||
assert "must be >= 0.0" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_get_val_boundary_values_are_accepted(monkeypatch):
|
||||
_fake_inputs(monkeypatch, ["0.025", "20.0"])
|
||||
assert LamNI._get_val("a", 0.0, float, min_=0.025, max_=20.0) == 0.025
|
||||
assert LamNI._get_val("b", 0.0, float, min_=0.025, max_=20.0) == 20.0
|
||||
Reference in new issue
Block a user