From 97d62f2f0b0762b6fc88e0fda978982944d29d3a Mon Sep 17 00:00:00 2001 From: x12sa Date: Tue, 10 Mar 2026 14:50:25 +0100 Subject: [PATCH] feat: check all devices are enabled, if not try to enable --- .../plugins/cSAXS/smaract.py | 133 +++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/cSAXS/smaract.py b/csaxs_bec/bec_ipython_client/plugins/cSAXS/smaract.py index 9481c1e..5274431 100644 --- a/csaxs_bec/bec_ipython_client/plugins/cSAXS/smaract.py +++ b/csaxs_bec/bec_ipython_client/plugins/cSAXS/smaract.py @@ -37,6 +37,92 @@ class cSAXSInitSmaractStages: # ------------------------------ # Internal helpers (runtime-based) # ------------------------------ + + def _ensure_all_session_devices_enabled(self, selection: set | None = None, try_enable: bool = True): + """ + Ensure all session devices (or a selection) that define 'bl_smar_stage' are enabled. + + Parameters + ---------- + selection : set | None + If provided, only devices in this set are considered. + try_enable : bool + If True, attempt to set device.enabled = True for devices that expose 'enabled' and are False. + If False, only report status without changing it. + + Returns + ------- + dict + { + "enabled_now": [device_names enabled by this call], + "already_enabled": [device_names already enabled or without 'enabled' attr], + "failed": [device_names that could not be enabled], + "inaccessible": [device_names not accessible] + } + """ + enabled_now = [] + already_enabled = [] + failed = [] + inaccessible = [] + + # Build axis map to restrict to SmarAct-based devices (same logic as other helpers) + axis_map = self._build_session_axis_map(selection=selection) + + for dev_name in sorted(axis_map.keys()): + try: + d = self._get_device_object(dev_name) + if d is None: + inaccessible.append(dev_name) + logger.warning(f"[cSAXS] Device {dev_name} not accessible.") + continue + + # If device has no 'enabled' attribute, treat as already enabled/usable + if not hasattr(d, "enabled"): + already_enabled.append(dev_name) + continue + + # If already enabled + try: + if getattr(d, "enabled"): + already_enabled.append(dev_name) + continue + except Exception: + # If reading enabled fails, treat as inaccessible for safety + failed.append(dev_name) + logger.warning(f"[cSAXS] Could not read 'enabled' for {dev_name}.") + continue + + # Device exists and is disabled + if try_enable: + try: + logger.info(f"[cSAXS] Enabling device {dev_name} (was disabled).") + setattr(d, "enabled", True) + # small delay to let device initialize if needed + time.sleep(0.05) + if getattr(d, "enabled"): + enabled_now.append(dev_name) + logger.info(f"[cSAXS] Device {dev_name} enabled.") + else: + failed.append(dev_name) + logger.warning(f"[cSAXS] Device {dev_name} still disabled after enabling attempt.") + except Exception as exc: + failed.append(dev_name) + logger.error(f"[cSAXS] Failed to enable {dev_name}: {exc}") + else: + # Not trying to enable, just report + failed.append(dev_name) + except Exception as exc: + failed.append(dev_name) + logger.error(f"[cSAXS] _ensure_all_session_devices_enabled error for {dev_name}: {exc}") + + return { + "enabled_now": enabled_now, + "already_enabled": already_enabled, + "failed": failed, + "inaccessible": inaccessible, + } + + def _yesno(self, question: str, default: str = "y") -> bool: """ Use OMNYTools.yesno if available; otherwise default to 'yes' (or fallback to input()). @@ -107,6 +193,7 @@ class cSAXSInitSmaractStages: # ------------------------------ # Public API # ------------------------------ + def smaract_reference_stages(self, force: bool = False, devices_to_reference=None): """ Reference SmarAct stages using runtime discovery. @@ -167,6 +254,19 @@ class cSAXSInitSmaractStages: devices_to_reference = [devices_to_reference] selection = set(devices_to_reference) if devices_to_reference else None + # First: ensure all relevant devices are enabled before attempting referencing + enable_report = self._ensure_all_session_devices_enabled(selection=selection, try_enable=True) + if enable_report["failed"]: + logger.warning( + "[cSAXS] Some devices could not be enabled before referencing: " + + ", ".join(sorted(enable_report["failed"])) + ) + if enable_report["inaccessible"]: + logger.warning( + "[cSAXS] Some devices were inaccessible before referencing: " + + ", ".join(sorted(enable_report["inaccessible"])) + ) + # Build axis map for selected devices (or all devices present) axis_map = self._build_session_axis_map(selection=selection) if selection: @@ -174,7 +274,6 @@ class cSAXSInitSmaractStages: if unknown: print(f"Unknown devices requested or missing 'bl_smar_stage' (ignored): {unknown}") - newly_referenced = [] already_referenced = [] failed = [] @@ -191,6 +290,17 @@ class cSAXSInitSmaractStages: failed.append(dev_name) continue + # If device exposes 'enabled' and is False, skip (we already tried enabling above) + try: + if hasattr(d, "enabled") and not getattr(d, "enabled"): + print(f"{dev_name}: device disabled, skipping.") + failed.append(dev_name) + continue + except Exception: + print(f"{dev_name}: could not read enabled state, skipping.") + failed.append(dev_name) + continue + try: is_ref = d.controller.axis_is_referenced(ch) @@ -246,7 +356,17 @@ class cSAXSInitSmaractStages: def smaract_check_all_referenced(self): """ Check reference state for all SmarAct devices that define 'bl_smar_stage'. + This now enables all relevant devices first (attempt), then performs the checks. """ + # Attempt to enable all relevant devices first (do not force enabling if you prefer) + enable_report = self._ensure_all_session_devices_enabled(selection=None, try_enable=True) + if enable_report["enabled_now"]: + print("Now enabled devices which were disabled before: " + ", ".join(sorted(enable_report["enabled_now"]))) + if enable_report["failed"]: + print("Could not enable: " + ", ".join(sorted(enable_report["failed"]))) + if enable_report["inaccessible"]: + print("Inaccessible: " + ", ".join(sorted(enable_report["inaccessible"]))) + axis_map = self._build_session_axis_map(selection=None) for dev_name in sorted(axis_map.keys()): ch = axis_map[dev_name] @@ -254,6 +374,16 @@ class cSAXSInitSmaractStages: if d is None: print(f"{dev_name}: device not accessible or unsupported.") continue + + # Skip devices that expose 'enabled' and are False + try: + if hasattr(d, "enabled") and not getattr(d, "enabled"): + print(f"{dev_name} (axis {ch}) is disabled; skipping reference check.") + continue + except Exception: + print(f"{dev_name} (axis {ch}) enabled-state unknown; skipping.") + continue + try: if d.controller.axis_is_referenced(ch): print(f"{dev_name} (axis {ch}) is referenced.") @@ -262,6 +392,7 @@ class cSAXSInitSmaractStages: except Exception as e: print(f"Error checking {dev_name} (axis {ch}): {e}") + def smaract_components_to_initial_position(self, devices_to_move=None): """ Move selected (or all) SmarAct-based components to their configured init_position.