test: Fix and add test scenarios for DeviceStatus error handling
This commit is contained in:
parent
87858edfe2
commit
4397db919a
@ -240,9 +240,11 @@ class CustomDetectorMixin:
|
|||||||
if result:
|
if result:
|
||||||
status.set_finished()
|
status.set_finished()
|
||||||
else:
|
else:
|
||||||
if self.stopped:
|
if self.parent.stopped:
|
||||||
|
# INFO This will execute a callback to the parent device.stop() method
|
||||||
status.set_exception(exc=DeviceStopError(f"{self.parent.name} was stopped"))
|
status.set_exception(exc=DeviceStopError(f"{self.parent.name} was stopped"))
|
||||||
else:
|
else:
|
||||||
|
# INFO This will execute a callback to the parent device.stop() method
|
||||||
status.set_exception(exc=exception_on_timeout)
|
status.set_exception(exc=exception_on_timeout)
|
||||||
# pylint: disable=broad-except
|
# pylint: disable=broad-except
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@ -250,6 +252,7 @@ class CustomDetectorMixin:
|
|||||||
logger.warning(
|
logger.warning(
|
||||||
f"Error in wait_for_signals in {self.parent.name}; Traceback: {content}"
|
f"Error in wait_for_signals in {self.parent.name}; Traceback: {content}"
|
||||||
)
|
)
|
||||||
|
# INFO This will execute a callback to the parent device.stop() method
|
||||||
status.set_exception(exc=exc)
|
status.set_exception(exc=exc)
|
||||||
|
|
||||||
thread = threading.Thread(
|
thread = threading.Thread(
|
||||||
|
@ -37,19 +37,16 @@ class SimCameraSetup(CustomDetectorMixin):
|
|||||||
status = DeviceStatus(self.parent)
|
status = DeviceStatus(self.parent)
|
||||||
|
|
||||||
def on_trigger_call(status: DeviceStatus) -> None:
|
def on_trigger_call(status: DeviceStatus) -> None:
|
||||||
error = None
|
|
||||||
try:
|
try:
|
||||||
for _ in range(self.parent.burst.get()):
|
for _ in range(self.parent.burst.get()):
|
||||||
data = self.parent.image.get()
|
data = self.parent.image.get()
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
self.parent._run_subs(sub_type=self.parent.SUB_MONITOR, value=data)
|
self.parent._run_subs(sub_type=self.parent.SUB_MONITOR, value=data)
|
||||||
if self.parent.stopped:
|
if self.parent.stopped:
|
||||||
error = DeviceStopError(f"{self.parent.name} was stopped")
|
raise DeviceStopError(f"{self.parent.name} was stopped")
|
||||||
break
|
|
||||||
if self.parent.write_to_disk.get():
|
if self.parent.write_to_disk.get():
|
||||||
self.parent.h5_writer.receive_data(data)
|
self.parent.h5_writer.receive_data(data)
|
||||||
# pylint: disable=expression-not-assigned
|
status.set_finished()
|
||||||
status.set_finished() if not error else status.set_exception(exc=error)
|
|
||||||
# pylint: disable=broad-except
|
# pylint: disable=broad-except
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
content = traceback.format_exc()
|
content = traceback.format_exc()
|
||||||
@ -93,15 +90,13 @@ class SimCameraSetup(CustomDetectorMixin):
|
|||||||
status = DeviceStatus(self.parent)
|
status = DeviceStatus(self.parent)
|
||||||
|
|
||||||
def on_complete_call(status: DeviceStatus) -> None:
|
def on_complete_call(status: DeviceStatus) -> None:
|
||||||
error = None
|
|
||||||
try:
|
try:
|
||||||
if self.parent.write_to_disk.get():
|
if self.parent.write_to_disk.get():
|
||||||
self.parent.h5_writer.write_data()
|
self.parent.h5_writer.write_data()
|
||||||
self.publish_file_location(done=True, successful=True)
|
self.publish_file_location(done=True, successful=True)
|
||||||
if self.parent.stopped:
|
if self.parent.stopped:
|
||||||
error = DeviceStopError(f"{self.parent.name} was stopped")
|
raise DeviceStopError(f"{self.parent.name} was stopped")
|
||||||
# pylint: disable=expression-not-assigned
|
status.set_finished()
|
||||||
status.set_finished() if not error else status.set_exception(exc=error)
|
|
||||||
# pylint: disable=broad-except
|
# pylint: disable=broad-except
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
content = traceback.format_exc()
|
content = traceback.format_exc()
|
||||||
|
@ -114,14 +114,12 @@ class SimMonitorAsyncPrepare(CustomDetectorMixin):
|
|||||||
status = DeviceStatus(self.parent)
|
status = DeviceStatus(self.parent)
|
||||||
|
|
||||||
def on_complete_call(status: DeviceStatus) -> None:
|
def on_complete_call(status: DeviceStatus) -> None:
|
||||||
error = None
|
|
||||||
try:
|
try:
|
||||||
if self.parent.data_buffer["value"]:
|
if self.parent.data_buffer["value"]:
|
||||||
self._send_data_to_bec()
|
self._send_data_to_bec()
|
||||||
if self.parent.stopped:
|
if self.parent.stopped:
|
||||||
error = DeviceStopError(f"{self.parent.name} was stopped")
|
raise DeviceStopError(f"{self.parent.name} was stopped")
|
||||||
# pylint: disable=expression-not-assigned
|
status.set_finished()
|
||||||
status.set_finished() if not error else status.set_exception(exc=error)
|
|
||||||
# pylint: disable=broad-except
|
# pylint: disable=broad-except
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
content = traceback.format_exc()
|
content = traceback.format_exc()
|
||||||
@ -157,7 +155,6 @@ class SimMonitorAsyncPrepare(CustomDetectorMixin):
|
|||||||
status = DeviceStatus(self.parent)
|
status = DeviceStatus(self.parent)
|
||||||
|
|
||||||
def on_trigger_call(status: DeviceStatus) -> None:
|
def on_trigger_call(status: DeviceStatus) -> None:
|
||||||
error = None
|
|
||||||
try:
|
try:
|
||||||
self.parent.data_buffer["value"].append(self.parent.readback.get())
|
self.parent.data_buffer["value"].append(self.parent.readback.get())
|
||||||
self.parent.data_buffer["timestamp"].append(self.parent.readback.timestamp)
|
self.parent.data_buffer["timestamp"].append(self.parent.readback.timestamp)
|
||||||
@ -166,9 +163,8 @@ class SimMonitorAsyncPrepare(CustomDetectorMixin):
|
|||||||
if self._counter % self._random_send_interval == 0:
|
if self._counter % self._random_send_interval == 0:
|
||||||
self._send_data_to_bec()
|
self._send_data_to_bec()
|
||||||
if self.parent.stopped:
|
if self.parent.stopped:
|
||||||
error = DeviceStopError(f"{self.parent.name} was stopped")
|
raise DeviceStopError(f"{self.parent.name} was stopped")
|
||||||
# pylint: disable=expression-not-assigned
|
status.set_finished()
|
||||||
status.set_finished() if not error else status.set_exception(exc=error)
|
|
||||||
# pylint: disable=broad-except
|
# pylint: disable=broad-except
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
content = traceback.format_exc()
|
content = traceback.format_exc()
|
||||||
|
@ -159,8 +159,6 @@ class SimPositioner(Device, PositionerBase):
|
|||||||
|
|
||||||
def _move_and_finish(self, start_pos, stop_pos, st):
|
def _move_and_finish(self, start_pos, stop_pos, st):
|
||||||
"""Move the simulated device and finish the motion."""
|
"""Move the simulated device and finish the motion."""
|
||||||
error = None
|
|
||||||
|
|
||||||
target = stop_pos + self.tolerance.get() * np.random.uniform(-1, 1)
|
target = stop_pos + self.tolerance.get() * np.random.uniform(-1, 1)
|
||||||
|
|
||||||
updates = np.ceil(np.abs(target - start_pos) / self.velocity.get() * self.update_frequency)
|
updates = np.ceil(np.abs(target - start_pos) / self.velocity.get() * self.update_frequency)
|
||||||
@ -170,12 +168,9 @@ class SimPositioner(Device, PositionerBase):
|
|||||||
ttime.sleep(1 / self.update_frequency)
|
ttime.sleep(1 / self.update_frequency)
|
||||||
self._update_state(ii)
|
self._update_state(ii)
|
||||||
if self._stopped:
|
if self._stopped:
|
||||||
error = DeviceStopError(f"{self.name} was stopped")
|
raise DeviceStopError(f"{self.parent.name} was stopped")
|
||||||
break
|
|
||||||
else:
|
|
||||||
self._update_state(target)
|
self._update_state(target)
|
||||||
# pylint: disable=expression-not-assigned
|
st.set_finished()
|
||||||
st.set_finished() if error is None else st.set_exception(error)
|
|
||||||
# pylint: disable=broad-except
|
# pylint: disable=broad-except
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
content = traceback.format_exc()
|
content = traceback.format_exc()
|
||||||
@ -231,7 +226,6 @@ class SimLinearTrajectoryPositioner(SimPositioner):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def _move_and_finish(self, start_pos, end_pos, st):
|
def _move_and_finish(self, start_pos, end_pos, st):
|
||||||
error = None
|
|
||||||
acc_time = (
|
acc_time = (
|
||||||
self.acceleration.get()
|
self.acceleration.get()
|
||||||
) # acceleration in Ophyd refers to acceleration time in seconds
|
) # acceleration in Ophyd refers to acceleration time in seconds
|
||||||
@ -243,18 +237,14 @@ class SimLinearTrajectoryPositioner(SimPositioner):
|
|||||||
while not traj.ended:
|
while not traj.ended:
|
||||||
ttime.sleep(1 / self.update_frequency)
|
ttime.sleep(1 / self.update_frequency)
|
||||||
self._update_state(traj.position())
|
self._update_state(traj.position())
|
||||||
if self._stopped:
|
|
||||||
error = DeviceStopError(f"{self.name} was stopped")
|
|
||||||
break
|
|
||||||
if self._stopped:
|
if self._stopped:
|
||||||
# simulate deceleration
|
# simulate deceleration
|
||||||
traj = stop_trajectory(traj)
|
traj = stop_trajectory(traj)
|
||||||
while not traj.ended:
|
while not traj.ended:
|
||||||
ttime.sleep(1 / self.update_frequency)
|
ttime.sleep(1 / self.update_frequency)
|
||||||
self._update_state(traj.position())
|
self._update_state(traj.position())
|
||||||
self._update_state(traj.position())
|
raise DeviceStopError(f"{self.parent.name} was stopped")
|
||||||
# pylint: disable=expression-not-assigned
|
st.set_finished()
|
||||||
st.set_finished() if error is None else st.set_exception(error)
|
|
||||||
# pylint: disable=broad-except
|
# pylint: disable=broad-except
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
content = traceback.format_exc()
|
content = traceback.format_exc()
|
||||||
|
@ -93,15 +93,12 @@ class SimWaveform(Device):
|
|||||||
status = DeviceStatus(self)
|
status = DeviceStatus(self)
|
||||||
|
|
||||||
def acquire(status: DeviceStatus):
|
def acquire(status: DeviceStatus):
|
||||||
error = None
|
|
||||||
try:
|
try:
|
||||||
for _ in range(self.burst.get()):
|
for _ in range(self.burst.get()):
|
||||||
self._run_subs(sub_type=self.SUB_MONITOR, value=self.waveform.get())
|
self._run_subs(sub_type=self.SUB_MONITOR, value=self.waveform.get())
|
||||||
if self.stopped:
|
if self.stopped:
|
||||||
error = DeviceStopError(f"{self.name} was stopped")
|
raise DeviceStopError(f"{self.name} was stopped")
|
||||||
break
|
status.set_finished()
|
||||||
# pylint: disable=expression-not-assigned
|
|
||||||
status.set_finished() if not error else status.set_exception(exc=error)
|
|
||||||
# pylint: disable=broad-except
|
# pylint: disable=broad-except
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
content = traceback.format_exc()
|
content = traceback.format_exc()
|
||||||
|
@ -11,6 +11,7 @@ from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
|||||||
PSIDetectorBase,
|
PSIDetectorBase,
|
||||||
)
|
)
|
||||||
from ophyd_devices.utils.bec_scaninfo_mixin import BecScaninfoMixin
|
from ophyd_devices.utils.bec_scaninfo_mixin import BecScaninfoMixin
|
||||||
|
from ophyd_devices.utils.errors import DeviceStopError, DeviceTimeoutError
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -113,13 +114,12 @@ def test_check_scan_id(detector_base):
|
|||||||
|
|
||||||
def test_wait_for_signal(detector_base):
|
def test_wait_for_signal(detector_base):
|
||||||
expected_value = "test"
|
expected_value = "test"
|
||||||
exception = TimeoutError("Timeout")
|
|
||||||
status = detector_base.custom_prepare.wait_with_status(
|
status = detector_base.custom_prepare.wait_with_status(
|
||||||
[(detector_base.filepath.get, expected_value)],
|
[(detector_base.filepath.get, expected_value)],
|
||||||
check_stopped=True,
|
check_stopped=True,
|
||||||
timeout=5,
|
timeout=5,
|
||||||
interval=0.01,
|
interval=0.01,
|
||||||
exception_on_timeout=exception,
|
exception_on_timeout=None,
|
||||||
)
|
)
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
assert status.done is False
|
assert status.done is False
|
||||||
@ -128,14 +128,14 @@ def test_wait_for_signal(detector_base):
|
|||||||
# some delay to allow the stop to take effect
|
# some delay to allow the stop to take effect
|
||||||
time.sleep(0.15)
|
time.sleep(0.15)
|
||||||
assert status.done is True
|
assert status.done is True
|
||||||
assert id(status.exception()) == id(exception)
|
assert status.exception().args == DeviceStopError(f"{detector_base.name} was stopped").args
|
||||||
detector_base.stopped = False
|
detector_base.stopped = False
|
||||||
status = detector_base.custom_prepare.wait_with_status(
|
status = detector_base.custom_prepare.wait_with_status(
|
||||||
[(detector_base.filepath.get, expected_value)],
|
[(detector_base.filepath.get, expected_value)],
|
||||||
check_stopped=True,
|
check_stopped=True,
|
||||||
timeout=5,
|
timeout=5,
|
||||||
interval=0.01,
|
interval=0.01,
|
||||||
exception_on_timeout=exception,
|
exception_on_timeout=None,
|
||||||
)
|
)
|
||||||
# Check that thread resolves when expected value is set
|
# Check that thread resolves when expected value is set
|
||||||
detector_base.filepath.set(expected_value)
|
detector_base.filepath.set(expected_value)
|
||||||
@ -144,3 +144,56 @@ def test_wait_for_signal(detector_base):
|
|||||||
assert status.done is True
|
assert status.done is True
|
||||||
assert status.success is True
|
assert status.success is True
|
||||||
assert status.exception() is None
|
assert status.exception() is None
|
||||||
|
|
||||||
|
detector_base.stopped = False
|
||||||
|
# Check that wait for status runs into timeout with expectd exception
|
||||||
|
detector_base.filepath.set("wrong_value")
|
||||||
|
exception = TimeoutError("Timeout")
|
||||||
|
status = detector_base.custom_prepare.wait_with_status(
|
||||||
|
[(detector_base.filepath.get, expected_value)],
|
||||||
|
check_stopped=True,
|
||||||
|
timeout=0.01,
|
||||||
|
interval=0.01,
|
||||||
|
exception_on_timeout=exception,
|
||||||
|
)
|
||||||
|
time.sleep(0.2)
|
||||||
|
assert status.done is True
|
||||||
|
assert id(status.exception()) == id(exception)
|
||||||
|
assert status.success is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_wait_for_signal_returns_exception(detector_base):
|
||||||
|
expected_value = "test"
|
||||||
|
# Check that wait for status runs into timeout with expectd exception
|
||||||
|
detector_base.filepath.set("wrong_value")
|
||||||
|
exception = TimeoutError("Timeout")
|
||||||
|
status = detector_base.custom_prepare.wait_with_status(
|
||||||
|
[(detector_base.filepath.get, expected_value)],
|
||||||
|
check_stopped=True,
|
||||||
|
timeout=0.01,
|
||||||
|
interval=0.01,
|
||||||
|
exception_on_timeout=exception,
|
||||||
|
)
|
||||||
|
time.sleep(0.2)
|
||||||
|
assert status.done is True
|
||||||
|
assert id(status.exception()) == id(exception)
|
||||||
|
assert status.success is False
|
||||||
|
|
||||||
|
detector_base.stopped = False
|
||||||
|
# Check that standard exception is thrown
|
||||||
|
status = detector_base.custom_prepare.wait_with_status(
|
||||||
|
[(detector_base.filepath.get, expected_value)],
|
||||||
|
check_stopped=True,
|
||||||
|
timeout=0.01,
|
||||||
|
interval=0.01,
|
||||||
|
exception_on_timeout=None,
|
||||||
|
)
|
||||||
|
time.sleep(0.2)
|
||||||
|
assert status.done is True
|
||||||
|
assert (
|
||||||
|
status.exception().args
|
||||||
|
== DeviceTimeoutError(
|
||||||
|
f"Timeout error for {detector_base.name} while waiting for signals {[(detector_base.filepath.get, expected_value)]}"
|
||||||
|
).args
|
||||||
|
)
|
||||||
|
assert status.success is False
|
||||||
|
Loading…
x
Reference in New Issue
Block a user