fix: fix occasional crash of mo1_bragg for scan; closes #11

This commit is contained in:
gac-x01da
2025-05-07 11:15:21 +02:00
parent 03e3b1c605
commit 32e24cd92a
2 changed files with 55 additions and 40 deletions

View File

@@ -14,7 +14,8 @@ from typing import Any, Literal
from bec_lib.devicemanager import ScanInfo
from bec_lib.logger import bec_logger
from ophyd import Component as Cpt
from ophyd import DeviceStatus, StatusBase
from ophyd import DeviceStatus, StatusBase, Signal
from ophyd.status import SubscriptionStatus
from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase
from ophyd_devices.utils.errors import DeviceStopError
from pydantic import BaseModel, Field
@@ -200,39 +201,39 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner):
# Load the scan parameters to the controller
self.scan_control.scan_load.put(1)
# Wait for params to be checked from controller
status = self.task_handler.submit_task(
task=self.wait_for_signal,
task_args=(self.scan_control.scan_msg, ScanControlLoadMessage.SUCCESS),
)
return status
self.wait_for_signal(self.scan_control.scan_msg, ScanControlLoadMessage.SUCCESS, timeout=self.timeout_for_pvwait)
return None
def on_unstage(self) -> DeviceStatus | StatusBase | None:
"""Called while unstaging the device."""
def unstage_procedure():
if self.stopped is True:
logger.warning(f"Resetting stopped in unstage for device {self.name}.")
self._stopped = False
current_state = self.scan_control.scan_msg.get()
# Case 1, message is already ScanControlLoadMessage.PENDING
if current_state == ScanControlLoadMessage.PENDING:
return None
# Case 2, probably called after scan, backend should resolve on its own. Timeout to wait
if current_state in [ScanControlLoadMessage.STARTED, ScanControlLoadMessage.SUCCESS]:
try:
self.wait_for_signal(
self.scan_control.scan_msg,
ScanControlLoadMessage.PENDING,
timeout=self.timeout_for_pvwait / 2,
timeout=self.timeout_for_pvwait,
)
return
except TimeoutError:
logger.warning(
f"Timeout after {self.timeout_for_pvwait} while waiting for scan validation"
f"Timeout in on_unstage of {self.name} after {self.timeout_for_pvwait}s, current scan_control_message : {self.scan_control.scan_msg.get()}"
)
time.sleep(0.25)
start_time = time.time()
while time.time() - start_time < self.timeout_for_pvwait / 2:
if not self.scan_control.scan_msg.get() == ScanControlLoadMessage.PENDING:
break
time.sleep(0.1)
raise TimeoutError(
f"Device {self.name} run into timeout after {self.timeout_for_pvwait} while waiting for scan validation"
)
status = self.task_handler.submit_task(unstage_procedure)
status.wait()
def callback(*, old_value, value, **kwargs):
if value == ScanControlLoadMessage.PENDING:
return True
return False
status = SubscriptionStatus(self.scan_control.scan_msg, callback=callback)
self.scan_control.scan_val_reset.put(1)
status.wait(timeout=self.timeout_for_pvwait)
return None
def on_pre_scan(self) -> DeviceStatus | StatusBase | None:
@@ -268,11 +269,13 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner):
if scan_duration < 0.1
else self.scan_control.scan_start_timer.put
)
def callback(*, old_value, value, **kwargs):
if old_value == ScanControlScanStatus.READY and value == ScanControlScanStatus.RUNNING:
return True
return False
status = SubscriptionStatus(self.scan_control.scan_status, callback=callback)
start_func(1)
status = self.task_handler.submit_task(
task=self.wait_for_signal,
task_args=(self.scan_control.scan_status, ScanControlScanStatus.RUNNING),
)
return status
def on_stop(self) -> None:
@@ -328,7 +331,7 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner):
time.sleep(0.1)
# If we end up here, the status did not resolve
raise TimeoutError(
f"Device {self.name} run into timeout after {timeout}s while waiting for scan to start"
f"Device {self.name} run into timeout after {timeout}s for signal {signal.name} with value {signal.get()}, expected {value}"
)
@typechecked
@@ -462,12 +465,23 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner):
f"Resetting scan validation in stage for state: {ScanControlLoadMessage(self.scan_control.scan_msg.get())}, "
f"retry .get() on scan_control: {ScanControlLoadMessage(self.scan_control.scan_msg.get())} and sleeping 1s"
)
self.scan_control.scan_val_reset.put(1)
current_scan_msg = self.scan_control.scan_msg.get()
def callback(*, old_value, value, **kwargs):
if old_value == current_scan_msg and value == target_state:
return True
return False
status = SubscriptionStatus(self.scan_control.scan_msg, callback=callback)
self.scan_control.scan_val_reset.put(1)
status.wait(timeout=self.timeout_for_pvwait)
# try:
# self.wait_for_signal(self.scan_control.scan_msg, target_state, timeout=4)
# except TimeoutError as exc:
# raise TimeoutError(
# f"Timeout after {self.timeout_for_pvwait} while waiting for scan status,"
# f" current state: {ScanControlScanStatus(self.scan_control.scan_msg.get())}"
# ) from exc
try:
self.wait_for_signal(self.scan_control.scan_msg, target_state, timeout=4)
except TimeoutError as exc:
raise TimeoutError(
f"Timeout after {self.timeout_for_pvwait} while waiting for scan status,"
f" current state: {ScanControlScanStatus(self.scan_control.scan_msg.get())}"
) from exc

View File

@@ -135,6 +135,7 @@ class Nidaq(PSIDeviceBase, NidaqControl):
def __init__(self, prefix: str = "", *, name: str, scan_info: ScanInfo = None, **kwargs):
super().__init__(name=name, prefix=prefix, scan_info=scan_info, **kwargs)
self.timeout_wait_for_signal = 5 # put 5s firsts
self._timeout_wait_for_pv = 3 # 3s timeout for pv calls
self.valid_scan_names = [
"xas_simple_scan",
"xas_simple_scan_with_xrd",
@@ -278,7 +279,7 @@ class Nidaq(PSIDeviceBase, NidaqControl):
timeout = self.timeout_wait_for_signal,
check_stopped=True):
raise NidaqError(f"Device {self.name} has not been reached in state STANDBY, current state {NidaqState(self.state.get())}")
self.scan_duration.set(0).wait()
self.scan_duration.set(0).wait(timeout=self._timeout_wait_for_pv)
def on_stage(self) -> DeviceStatus | StatusBase | None:
"""
@@ -296,9 +297,9 @@ class Nidaq(PSIDeviceBase, NidaqControl):
raise NidaqError(
f"Device {self.name} has not been reached in state STANDBY, current state {NidaqState(self.state.get())}"
)
self.scan_type.set(ScanType.TRIGGERED).wait()
self.scan_duration.set(0).wait()
self.stage_call.set(1).wait()
self.scan_type.set(ScanType.TRIGGERED).wait(timeout = self._timeout_wait_for_pv)
self.scan_duration.set(0).wait(timeout=self._timeout_wait_for_pv)
self.stage_call.set(1).wait(timeout = self._timeout_wait_for_pv)
if not self.wait_for_condition(
condition=lambda: self.state.get() == NidaqState.STAGE, timeout=self.timeout_wait_for_signal, check_stopped=True
@@ -306,7 +307,7 @@ class Nidaq(PSIDeviceBase, NidaqControl):
raise NidaqError(
f"Device {self.name} has not been reached in state STAGE, current state {NidaqState(self.state.get())}"
)
self.kickoff_call.set(1).wait()
self.kickoff_call.set(1).wait(timeout = self._timeout_wait_for_pv)
logger.info(f"Device {self.name} was staged: {NidaqState(self.state.get())}")
def on_unstage(self) -> DeviceStatus | StatusBase | None:
@@ -374,4 +375,4 @@ class Nidaq(PSIDeviceBase, NidaqControl):
def on_stop(self) -> None:
"""Called when the device is stopped."""
self.stop_call.set(1).wait()
self.stop_call.put(1)