mirror of
https://github.com/bec-project/ophyd_devices.git
synced 2025-06-23 11:27:57 +02:00
feat(psi device base): stoppable status objects
Add methods to PSIDeviceBase to register status object that should be cancelled when the device is stopped or destroyed.
This commit is contained in:
@ -64,6 +64,7 @@ class PSIDeviceBase(Device):
|
||||
else:
|
||||
super().__init__(prefix=prefix, name=name, **kwargs)
|
||||
self._stopped = False
|
||||
self._stoppable_status_objects: list[StatusBase] = []
|
||||
self.task_handler = TaskHandler(parent=self)
|
||||
self.file_utils = FileHandler()
|
||||
if scan_info is None:
|
||||
@ -113,6 +114,7 @@ class PSIDeviceBase(Device):
|
||||
"""Unstage the device."""
|
||||
super_unstage = super().unstage()
|
||||
status = self.on_unstage() # pylint: disable=assignment-from-no-return
|
||||
self._stop_stoppable_status_objects()
|
||||
if isinstance(status, StatusBase):
|
||||
return status
|
||||
return super_unstage
|
||||
@ -154,13 +156,52 @@ class PSIDeviceBase(Device):
|
||||
"""
|
||||
self.on_stop()
|
||||
self.stopped = True # Set stopped flag to True, in case a custom stop method listens to stopped property
|
||||
# Stop all stoppable status objects
|
||||
self._stop_stoppable_status_objects()
|
||||
super().stop(success=success)
|
||||
|
||||
def destroy(self):
|
||||
"""Destroy the device."""
|
||||
self.on_destroy() # Call the on_destroy method
|
||||
self._stop_stoppable_status_objects()
|
||||
return super().destroy()
|
||||
|
||||
########################################
|
||||
# Stoppable Status Objects Management #
|
||||
########################################
|
||||
|
||||
def cancel_on_stop(self, status: StatusBase) -> None:
|
||||
"""
|
||||
Register a status object to be cancelled when the device is stopped.
|
||||
|
||||
Args:
|
||||
status (StatusBase): The status object to be cancelled.
|
||||
"""
|
||||
if not isinstance(status, StatusBase):
|
||||
raise TypeError("status must be an instance of StatusBase")
|
||||
self._stoppable_status_objects.append(status)
|
||||
|
||||
def _clear_stoppable_status_objects(self) -> None:
|
||||
"""
|
||||
Clear all registered stoppable status objects.
|
||||
|
||||
This is useful to reset the list of status objects that should be cancelled
|
||||
when the device is stopped.
|
||||
"""
|
||||
self._stoppable_status_objects = []
|
||||
|
||||
def _stop_stoppable_status_objects(self) -> None:
|
||||
"""
|
||||
Stop all registered stoppable status objects.
|
||||
|
||||
This method will cancel all status objects that have been registered
|
||||
to be stopped when the device is stopped.
|
||||
"""
|
||||
for status in self._stoppable_status_objects:
|
||||
if not status.done:
|
||||
status.set_exception(DeviceStoppedError(f"Device {self.name} has been stopped"))
|
||||
self._clear_stoppable_status_objects()
|
||||
|
||||
########################################
|
||||
# Utility Method to wait for signals #
|
||||
########################################
|
||||
|
@ -1,5 +1,7 @@
|
||||
"""Module for testing the PSIDeviceBase class."""
|
||||
|
||||
import threading
|
||||
import time
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
@ -146,3 +148,31 @@ def test_on_stop_hook(device):
|
||||
with mock.patch.object(device, "on_stop") as mock_on_stop:
|
||||
device.stop()
|
||||
mock_on_stop.assert_called_once()
|
||||
|
||||
|
||||
def test_stoppable_status(device):
|
||||
"""Test stoppable status"""
|
||||
status = StatusBase()
|
||||
device.cancel_on_stop(status)
|
||||
device.stop()
|
||||
assert status.done is True
|
||||
assert status.success is False
|
||||
|
||||
|
||||
def test_stoppable_status_not_done(device):
|
||||
"""Test stoppable status not done"""
|
||||
|
||||
def stop_after_delay():
|
||||
time.sleep(5)
|
||||
device.stop()
|
||||
|
||||
status = StatusBase()
|
||||
device.cancel_on_stop(status)
|
||||
thread = threading.Thread(target=stop_after_delay)
|
||||
thread.start()
|
||||
|
||||
with pytest.raises(DeviceStoppedError, match="Device device has been stopped"):
|
||||
status.wait()
|
||||
|
||||
assert status.done is True
|
||||
assert status.success is False
|
||||
|
Reference in New Issue
Block a user