diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md index eb9d1348..e3417883 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md @@ -70,11 +70,10 @@ default and is in no profile (flOMNI `FeedbackLoop/soft_trigger.md`). **`flomni_fermat_scan` sets the trigger source at every scan start** (2026-10-26): - **At the beamline → hardware.** At the beamline means: `ddg1` is configured with a real (not simulated) device - class, or one of the exposure box motors `ebtrx`, `ebtry`, `ebtrz`, `ebsupport` is in the session. If this cannot - be confirmed (web HMI not reachable, refused) the scan is aborted: with the soft trigger the detector would not - be triggered while flOMNI exposes internally. A failing check also counts as beamline. -- **Otherwise (simulated `ddg1`, no beamline devices) → soft** with `soft_exposure_ms` = `exp_time` × - `frames_per_trigger` × 1000. If that fails the scan continues with a warning (alarm). + class (only `ddg1` decides, 2026-10-27). If this cannot be confirmed (web HMI not reachable, refused) the scan is + aborted: with the soft trigger the detector would not be triggered while flOMNI exposes internally. A failing check + also counts as beamline. +- **Otherwise (simulated or no `ddg1`) → soft** with `soft_exposure_ms` = `exp_time` × `frames_per_trigger` × 1000. If that fails the scan continues with a warning (alarm). By hand: `flomni.rt_settings.trigger_set_soft(True, exposure_ms=100)`, `trigger_set_soft(False)`; `trigger_show()` shows both groups. diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.pdf b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.pdf index fb4ee1d0..f8f45098 100644 Binary files a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.pdf and b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.pdf differ diff --git a/csaxs_bec/scans/flomni_fermat_scan.py b/csaxs_bec/scans/flomni_fermat_scan.py index 6cfac1bc..8d056a2a 100644 --- a/csaxs_bec/scans/flomni_fermat_scan.py +++ b/csaxs_bec/scans/flomni_fermat_scan.py @@ -69,10 +69,6 @@ class FlomniFermatScan(ScanBase): MAX_CORRIDOR_SIZE = 3 # Corridor size is capped at this value (in um) for stability MIN_FERMAT_ASYMMETRY = 0.1 # Practical working range for fermat_asymmetry (soft-clamped) MAX_FERMAT_ASYMMETRY = 10.0 - # Devices that exist only in the beamline configuration (bl_endstation.yaml: exposure box motors); with one of - # them, or a ddg1 that is not simulated, the session is at the beamline -> hardware trigger (flOMNI soft trigger, - # FeedbackLoop/soft_trigger.md, 2026-10-26) - BEAMLINE_DEVICES = ("ebtrx", "ebtry", "ebtrz", "ebsupport") def __init__( # fmt: off @@ -386,26 +382,20 @@ class FlomniFermatScan(ScanBase): def at_beamline(self) -> tuple[bool, str]: """ - Is this session at the beamline? True when ddg1 is configured with a real (not simulated) device class or - one of BEAMLINE_DEVICES is configured; False only when both say no. A failing check counts as beamline, - so the hardware trigger is the fallback. + Is this session at the beamline? True when ddg1 is configured with a real (not simulated) device class + (flOMNI soft trigger, FeedbackLoop/soft_trigger.md; ddg1 only, Mirko 2026-10-27). A failing check counts as + beamline, so the hardware trigger is the fallback. Returns: (at_beamline, reason) """ try: devices = self.device_manager.devices - reasons = [] if "ddg1" in devices: cls = str((getattr(devices["ddg1"], "_config", None) or {}).get("deviceClass", "")) if ".sim." not in cls and "Simulated" not in cls: - reasons.append(f"ddg1 is {cls or 'not simulated'}") - found = [name for name in self.BEAMLINE_DEVICES if name in devices] - if found: - reasons.append("beamline devices " + ", ".join(found)) - if reasons: - return True, "; ".join(reasons) - return False, "no beamline devices, ddg1 simulated or absent" + return True, f"ddg1 is {cls or 'not simulated'}" + return False, "ddg1 simulated or absent" except Exception as exc: # pylint: disable=broad-except return True, f"beamline check failed ({exc}), treated as beamline" diff --git a/tests/tests_scans/test_flomni_fermat_scan.py b/tests/tests_scans/test_flomni_fermat_scan.py index 98a66823..c68101a8 100644 --- a/tests/tests_scans/test_flomni_fermat_scan.py +++ b/tests/tests_scans/test_flomni_fermat_scan.py @@ -162,7 +162,6 @@ def _trigger_stub(devices, apply_result=None, apply_exc=None, exp_time=0.1, fram """A stand-in for a FlomniFermatScan instance with a device list {name: deviceClass} and the loop-settings device answering /api/settings/apply.""" scan = mock.MagicMock() - scan.BEAMLINE_DEVICES = FlomniFermatScan.BEAMLINE_DEVICES scan.device_manager.devices = { name: mock.MagicMock(_config={"deviceClass": cls}) for name, cls in devices.items() } @@ -187,9 +186,8 @@ def test_at_beamline_detection(): assert FlomniFermatScan.at_beamline(_trigger_stub({"ddg1": SIM_DDG1}))[0] is False assert FlomniFermatScan.at_beamline(_trigger_stub({}))[0] is False assert FlomniFermatScan.at_beamline(_trigger_stub({"ddg1": REAL_DDG1}))[0] is True - # a beamline-only device counts even with a simulated ddg1 - at_bl, reason = FlomniFermatScan.at_beamline(_trigger_stub({"ddg1": SIM_DDG1, "ebtrx": MOTOR})) - assert at_bl is True and "ebtrx" in reason + # only ddg1 decides: other devices (e.g. exposure box motors) do not count + assert FlomniFermatScan.at_beamline(_trigger_stub({"ddg1": SIM_DDG1, "ebtrx": MOTOR}))[0] is False # a failing check counts as beamline (hardware trigger is the fallback) scan = _trigger_stub({}) scan.device_manager.devices = None