Skip to content
Merged
2 changes: 1 addition & 1 deletion src/dodal/beamlines/b07.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
B07SampleManipulator52B,
Grating,
LensMode,
PsuMode,
)
from dodal.devices.beamlines.b07_shared import PsuMode
from dodal.devices.electron_analyser.base import EnergySource
from dodal.devices.electron_analyser.specs import SpecsDetector
from dodal.devices.motors import XYZPolarStage
Expand Down
2 changes: 1 addition & 1 deletion src/dodal/beamlines/b07_1.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from dodal.beamlines.b07_shared import devices as b07_shared_devices
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
from dodal.device_manager import DeviceManager
from dodal.devices.beamlines.b07 import PsuMode
from dodal.devices.beamlines.b07_1 import (
ChannelCutMonochromator,
Grating,
LensMode,
)
from dodal.devices.beamlines.b07_shared import PsuMode
from dodal.devices.electron_analyser.base import EnergySource
from dodal.devices.electron_analyser.specs import SpecsDetector
from dodal.devices.motors import XYZPolarAzimuthStage
Expand Down
4 changes: 2 additions & 2 deletions src/dodal/devices/beamlines/b07/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .b07_motors import B07SampleManipulator52B
from .enums import Grating, LensMode, PsuMode
from .enums import Grating, LensMode

__all__ = ["B07SampleManipulator52B", "Grating", "LensMode", "PsuMode"]
__all__ = ["B07SampleManipulator52B", "Grating", "LensMode"]
12 changes: 0 additions & 12 deletions src/dodal/devices/beamlines/b07/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,3 @@ class LensMode(SupersetEnum):
# option if disconnected. Once it is connected, "Not connected" is replaced with the
# options above. This is also why this must be a SupersetEnum.
NOT_CONNECTED = "Not connected"


class PsuMode(SupersetEnum):
V3500 = "3.5kV"
V1500 = "1.5kV"
V400 = "400V"
V100 = "100V"
V10 = "10V"
# This is connected to the device separately and will only have "Not connected" as
# option if disconnected. Once it is connected, "Not connected" is replaced with the
# options above. This is also why this must be a SupersetEnum.
NOT_CONNECTED = "Not connected"
3 changes: 3 additions & 0 deletions src/dodal/devices/beamlines/b07_shared/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .enums import PsuMode

__all__ = ["PsuMode"]
13 changes: 13 additions & 0 deletions src/dodal/devices/beamlines/b07_shared/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from ophyd_async.core import SupersetEnum


class PsuMode(SupersetEnum):
V3500 = "3.5kV"
V1500 = "1.5kV"
V400 = "400V"
V100 = "100V"
V10 = "10V"
# This is connected to the device separately and will only have "Not connected" as
# option if disconnected. Once it is connected, "Not connected" is replaced with the
# options above. This is also why this must be a SupersetEnum.
NOT_CONNECTED = "Not connected"
51 changes: 13 additions & 38 deletions src/dodal/devices/electron_analyser/base/base_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
TriggerInfo,
)

from dodal.common.data_util import load_json_file_to_class
from dodal.devices.electron_analyser.base.base_controller import (
ElectronAnalyserController,
)
Expand All @@ -20,9 +19,7 @@
)
from dodal.devices.electron_analyser.base.base_region import (
GenericRegion,
GenericSequence,
TAbstractBaseRegion,
TAbstractBaseSequence,
)


Expand Down Expand Up @@ -111,6 +108,9 @@ async def trigger(self) -> None:
await super().trigger()


# Used in sm-bluesky, but will hopefully be removed along with
# ElectronAnalyserRegionDetector in future. Blocked by:
# https://github.com/bluesky/bluesky/pull/1978
GenericElectronAnalyserRegionDetector = ElectronAnalyserRegionDetector[
GenericAnalyserDriverIO, GenericRegion
]
Expand All @@ -123,24 +123,13 @@ async def trigger(self) -> None:
class ElectronAnalyserDetector(
BaseElectronAnalyserDetector[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
Stageable,
Generic[TAbstractBaseSequence, TAbstractAnalyserDriverIO, TAbstractBaseRegion],
Generic[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
):
"""Electron analyser detector with the additional functionality to load a sequence
file and create a list of temporary ElectronAnalyserRegionDetector objects. These
will setup configured region settings before data acquisition.
"""

def __init__(
self,
sequence_class: type[TAbstractBaseSequence],
controller: ElectronAnalyserController[
TAbstractAnalyserDriverIO, TAbstractBaseRegion
],
name: str = "",
):
self._sequence_class = sequence_class
super().__init__(controller, name)

@AsyncStatus.wrap
async def stage(self) -> None:
"""Prepare the detector for use by ensuring it is idle and ready.
Expand All @@ -160,38 +149,24 @@ async def unstage(self) -> None:
"""Disarm the detector."""
await self._controller.disarm()

def load_sequence(self, filename: str) -> TAbstractBaseSequence:
"""Load the sequence data from a provided json file into a sequence class.

Args:
filename (str): Path to the sequence file containing the region data.

Returns:
Pydantic model representing the sequence file.
"""
return load_json_file_to_class(self._sequence_class, filename)

def create_region_detector_list(
self, filename: str, enabled_only=True
self, regions: list[TAbstractBaseRegion]
) -> list[
ElectronAnalyserRegionDetector[TAbstractAnalyserDriverIO, TAbstractBaseRegion]
]:
"""Create a list of detectors equal to the number of regions in a sequence file.
Each detector is responsible for setting up a specific region.
"""This method can hopefully be dropped when this is merged and released.
https://github.com/bluesky/bluesky/pull/1978.

Create a list of detectors equal to the number of regions. Each detector is
responsible for setting up a specific region.

Args:
filename (str): Path to the sequence file containing the region data.
enabled_only (bool, optional): If true, only include the region if enabled
is True.
regions: The list of regions to give to each region detector.

Returns:
List of ElectronAnalyserRegionDetector, equal to the number of regions in
the sequence file.
the sequence file.
"""
seq = self.load_sequence(filename)
regions: list[TAbstractBaseRegion] = (
seq.get_enabled_regions() if enabled_only else seq.regions
)
return [
ElectronAnalyserRegionDetector[
TAbstractAnalyserDriverIO, TAbstractBaseRegion
Expand All @@ -201,7 +176,7 @@ def create_region_detector_list(


GenericElectronAnalyserDetector = ElectronAnalyserDetector[
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you need this class here? it seems to be used only in tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this will be used in plan repo too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which plan repo? sm-bluesky?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm again in tests...:)

GenericSequence, GenericAnalyserDriverIO, GenericRegion
GenericAnalyserDriverIO, GenericRegion
]
TElectronAnalyserDetector = TypeVar(
"TElectronAnalyserDetector",
Expand Down
13 changes: 3 additions & 10 deletions src/dodal/devices/electron_analyser/specs/specs_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,13 @@
from dodal.devices.electron_analyser.base.base_region import TLensMode, TPsuMode
from dodal.devices.electron_analyser.base.energy_sources import AbstractEnergySource
from dodal.devices.electron_analyser.specs.specs_driver_io import SpecsAnalyserDriverIO
from dodal.devices.electron_analyser.specs.specs_region import (
SpecsRegion,
SpecsSequence,
)
from dodal.devices.electron_analyser.specs.specs_region import SpecsRegion
from dodal.devices.fast_shutter import FastShutter
from dodal.devices.selectable_source import SourceSelector


class SpecsDetector(
ElectronAnalyserDetector[
SpecsSequence[TLensMode, TPsuMode],
SpecsAnalyserDriverIO[TLensMode, TPsuMode],
SpecsRegion[TLensMode, TPsuMode],
],
Expand All @@ -33,15 +29,12 @@ def __init__(
source_selector: SourceSelector | None = None,
name: str = "",
):
# Save to class so takes part with connect()
# Make attribute of class so connect applies to driver and populates parent.
self.driver = SpecsAnalyserDriverIO[TLensMode, TPsuMode](
prefix, lens_mode_type, psu_mode_type
)

controller = ElectronAnalyserController[
SpecsAnalyserDriverIO[TLensMode, TPsuMode], SpecsRegion[TLensMode, TPsuMode]
](self.driver, energy_source, shutter, source_selector)

sequence_class = SpecsSequence[lens_mode_type, psu_mode_type]

super().__init__(sequence_class, controller, name)
super().__init__(controller, name)
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@
from dodal.devices.electron_analyser.vgscienta.vgscienta_region import (
TPassEnergyEnum,
VGScientaRegion,
VGScientaSequence,
)
from dodal.devices.fast_shutter import FastShutter
from dodal.devices.selectable_source import SourceSelector


class VGScientaDetector(
ElectronAnalyserDetector[
VGScientaSequence[TLensMode, TPsuMode, TPassEnergyEnum],
VGScientaAnalyserDriverIO[TLensMode, TPsuMode, TPassEnergyEnum],
VGScientaRegion[TLensMode, TPassEnergyEnum],
],
Expand All @@ -37,17 +35,13 @@ def __init__(
source_selector: SourceSelector | None = None,
name: str = "",
):
# Save to class so takes part with connect()
# Make attribute of class so connect applies to driver and populates parent.
self.driver = VGScientaAnalyserDriverIO[TLensMode, TPsuMode, TPassEnergyEnum](
prefix, lens_mode_type, psu_mode_type, pass_energy_type
)

controller = ElectronAnalyserController[
VGScientaAnalyserDriverIO[TLensMode, TPsuMode, TPassEnergyEnum],
VGScientaRegion[TLensMode, TPassEnergyEnum],
](self.driver, energy_source, shutter, source_selector)

sequence_class = VGScientaSequence[
lens_mode_type, psu_mode_type, pass_energy_type
]
super().__init__(sequence_class, controller, name)
super().__init__(controller, name)
6 changes: 0 additions & 6 deletions src/dodal/testing/electron_analyser/__init__.py

This file was deleted.

57 changes: 0 additions & 57 deletions src/dodal/testing/electron_analyser/device_factory.py

This file was deleted.

20 changes: 20 additions & 0 deletions tests/devices/electron_analyser/base/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import pytest

from dodal.devices.beamlines import b07, b07_shared, i09
from dodal.devices.electron_analyser.base import GenericElectronAnalyserDetector
from dodal.devices.electron_analyser.specs import SpecsDetector
from dodal.devices.electron_analyser.vgscienta import VGScientaDetector


@pytest.fixture(params=["ew4000", "b07b_specs150"])
def sim_detector(
request: pytest.FixtureRequest,
ew4000: VGScientaDetector[i09.LensMode, i09.PsuMode, i09.PassEnergy],
b07b_specs150: SpecsDetector[b07.LensMode, b07_shared.PsuMode],
) -> GenericElectronAnalyserDetector:
detectors = [ew4000, b07b_specs150]
for detector in detectors:
if detector.name == request.param:
return detector

raise ValueError(f"Detector with name '{request.param}' not found")
Loading