Skip to content
16 changes: 8 additions & 8 deletions src/dodal/devices/electron_analyser/base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
)
from .base_enums import EnergyMode
from .base_region import (
AbstractBaseRegion,
AbstractBaseSequence,
BaseRegion,
BaseSequence,
GenericRegion,
GenericSequence,
TAbstractBaseRegion,
TAbstractBaseSequence,
TAcquisitionMode,
TBaseRegion,
TBaseSequence,
TLensMode,
)
from .base_util import to_binding_energy, to_kinetic_energy
Expand All @@ -42,12 +42,12 @@
"GenericAnalyserDriverIO",
"TAbstractAnalyserDriverIO",
"EnergyMode",
"AbstractBaseRegion",
"AbstractBaseSequence",
"BaseRegion",
"BaseSequence",
"GenericRegion",
"GenericSequence",
"TAbstractBaseRegion",
"TAbstractBaseSequence",
"TBaseRegion",
"TBaseSequence",
"TAcquisitionMode",
"TLensMode",
"to_binding_energy",
Expand Down
6 changes: 3 additions & 3 deletions src/dodal/devices/electron_analyser/base/base_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
)
from dodal.devices.electron_analyser.base.base_region import (
GenericRegion,
TAbstractBaseRegion,
TBaseRegion,
)
from dodal.devices.electron_analyser.base.energy_sources import AbstractEnergySource
from dodal.devices.fast_shutter import GenericFastShutter
Expand All @@ -19,7 +19,7 @@

class ElectronAnalyserController(
ConstantDeadTimeController[TAbstractAnalyserDriverIO],
Generic[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
Generic[TAbstractAnalyserDriverIO, TBaseRegion],
):
"""Specialised controller for the electron analysers to provide additional setup
logic such as selecting the energy source to use from requested region and giving
Expand Down Expand Up @@ -50,7 +50,7 @@ def __init__(
self.source_selector = source_selector
super().__init__(driver, deadtime, image_mode)

async def setup_with_region(self, region: TAbstractBaseRegion) -> None:
async def setup_with_region(self, region: TBaseRegion) -> None:
"""Logic to set the driver with a region."""
if self.source_selector is not None:
await self.source_selector.set(region.excitation_energy_source)
Expand Down
36 changes: 15 additions & 21 deletions src/dodal/devices/electron_analyser/base/base_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
)
from dodal.devices.electron_analyser.base.base_region import (
GenericRegion,
TAbstractBaseRegion,
TBaseRegion,
)


Expand All @@ -28,7 +28,7 @@ class BaseElectronAnalyserDetector(
Triggerable,
AsyncReadable,
AsyncConfigurable,
Generic[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
Generic[TAbstractAnalyserDriverIO, TBaseRegion],
):
"""Detector for data acquisition of electron analyser. Can only acquire using
settings already configured for the device.
Expand All @@ -40,16 +40,14 @@ class BaseElectronAnalyserDetector(

def __init__(
self,
controller: ElectronAnalyserController[
TAbstractAnalyserDriverIO, TAbstractBaseRegion
],
controller: ElectronAnalyserController[TAbstractAnalyserDriverIO, TBaseRegion],
name: str = "",
):
self._controller = controller
super().__init__(name)

@AsyncStatus.wrap
async def set(self, region: TAbstractBaseRegion) -> None:
async def set(self, region: TBaseRegion) -> None:
await self._controller.setup_with_region(region)

@AsyncStatus.wrap
Expand Down Expand Up @@ -83,19 +81,17 @@ async def describe_configuration(self) -> dict[str, DataKey]:


class ElectronAnalyserRegionDetector(
BaseElectronAnalyserDetector[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
Generic[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
BaseElectronAnalyserDetector[TAbstractAnalyserDriverIO, TBaseRegion],
Generic[TAbstractAnalyserDriverIO, TBaseRegion],
):
"""Extends electron analyser detector to configure specific region settings before
data acquisition. It is designed to only exist inside a plan.
"""

def __init__(
self,
controller: ElectronAnalyserController[
TAbstractAnalyserDriverIO, TAbstractBaseRegion
],
region: TAbstractBaseRegion,
controller: ElectronAnalyserController[TAbstractAnalyserDriverIO, TBaseRegion],
region: TBaseRegion,
name: str = "",
):
self.region = region
Expand All @@ -121,9 +117,9 @@ async def trigger(self) -> None:


class ElectronAnalyserDetector(
BaseElectronAnalyserDetector[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
BaseElectronAnalyserDetector[TAbstractAnalyserDriverIO, TBaseRegion],
Stageable,
Generic[TAbstractAnalyserDriverIO, TAbstractBaseRegion],
Generic[TAbstractAnalyserDriverIO, TBaseRegion],
):
"""Electron analyser detector with the additional functionality to load a sequence
file and create a list of temporary ElectronAnalyserRegionDetector objects. These
Expand All @@ -150,10 +146,8 @@ async def unstage(self) -> None:
await self._controller.disarm()

def create_region_detector_list(
self, regions: list[TAbstractBaseRegion]
) -> list[
ElectronAnalyserRegionDetector[TAbstractAnalyserDriverIO, TAbstractBaseRegion]
]:
self, regions: list[TBaseRegion]
) -> list[ElectronAnalyserRegionDetector[TAbstractAnalyserDriverIO, TBaseRegion]]:
"""This method can hopefully be dropped when this is merged and released.
https://github.com/bluesky/bluesky/pull/1978.

Expand All @@ -168,9 +162,9 @@ def create_region_detector_list(
the sequence file.
"""
return [
ElectronAnalyserRegionDetector[
TAbstractAnalyserDriverIO, TAbstractBaseRegion
](self._controller, r, self.name + "_" + r.name)
ElectronAnalyserRegionDetector[TAbstractAnalyserDriverIO, TBaseRegion](
self._controller, r, self.name + "_" + r.name
)
for r in regions
]

Expand Down
10 changes: 5 additions & 5 deletions src/dodal/devices/electron_analyser/base/base_driver_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
AnyLensMode,
AnyPassEnergy,
GenericRegion,
TAbstractBaseRegion,
TAcquisitionMode,
TBaseRegion,
TLensMode,
TPassEnergy,
)
Expand All @@ -40,8 +40,8 @@ class AbstractAnalyserDriverIO(
ABC,
StandardReadable,
ADBaseIO,
Movable[TAbstractBaseRegion],
Generic[TAbstractBaseRegion, TAcquisitionMode, TLensMode, TPsuMode, TPassEnergy],
Movable[TBaseRegion],
Generic[TBaseRegion, TAcquisitionMode, TLensMode, TPsuMode, TPassEnergy],
):
"""Driver device that defines signals and readables that should be common to all
electron analysers. Implementations of electron analyser devices should inherit
Expand Down Expand Up @@ -137,12 +137,12 @@ def __init__(

@abstractmethod
@AsyncStatus.wrap
async def set(self, epics_region: TAbstractBaseRegion):
async def set(self, epics_region: TBaseRegion):
"""Move a group of signals defined in a region. Each implementation of this
class is responsible for implementing this method correctly.

Args:
epics_region (TAbstractBaseRegion): Contains the parameters to setup the
epics_region (TBaseRegion): Contains the parameters to setup the
driver for a scan.
"""

Expand Down
25 changes: 13 additions & 12 deletions src/dodal/devices/electron_analyser/base/base_region.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import Generic, Self, TypeAlias, TypeVar

from ophyd_async.core import StrictEnum, SupersetEnum
from pydantic import BaseModel, Field, model_validator
from pydantic import BaseModel, ConfigDict, Field, model_validator

from dodal.devices.electron_analyser.base.base_enums import EnergyMode
from dodal.devices.electron_analyser.base.base_util import (
Expand Down Expand Up @@ -64,7 +64,7 @@ def energy_mode_validation(data: dict) -> dict:
return data


class AbstractBaseRegion(
class BaseRegion(
ABC,
JavaToPythonModel,
Generic[TAcquisitionMode, TLensMode, TPassEnergy],
Expand All @@ -73,6 +73,8 @@ class AbstractBaseRegion(
inherit this to extend functionality. All energy units are assumed to be in eV.
"""

model_config = ConfigDict(populate_by_name=True)

name: str = "New_region"
enabled: bool = False
slices: int = 1
Expand Down Expand Up @@ -166,24 +168,23 @@ def before_validation(cls, data: dict) -> dict:
return energy_mode_validation(data)


GenericRegion = AbstractBaseRegion[AnyAcqMode, AnyLensMode, AnyPassEnergy]
TAbstractBaseRegion = TypeVar("TAbstractBaseRegion", bound=AbstractBaseRegion)
GenericRegion = BaseRegion[AnyAcqMode, AnyLensMode, AnyPassEnergy]
TBaseRegion = TypeVar("TBaseRegion", bound=BaseRegion)


class AbstractBaseSequence(
class BaseSequence(
ABC,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

remove ABC if it is not abstract anymore?

JavaToPythonModel,
Generic[TAbstractBaseRegion],
Generic[TBaseRegion],
):
"""Generic sequence model that holds the list of region data. Specialised sequence
models should inherit this to extend functionality and define type of region to
hold.
"""

version: float = 0.1 # If file format changes within prod, increment this number!
regions: list[TAbstractBaseRegion] = Field(default_factory=lambda: [])
regions: list[TBaseRegion] = Field(default_factory=lambda: [])

def get_enabled_regions(self) -> list[TAbstractBaseRegion]:
def get_enabled_regions(self) -> list[TBaseRegion]:
return [r for r in self.regions if r.enabled]

def get_region_names(self) -> list[str]:
Expand All @@ -192,9 +193,9 @@ def get_region_names(self) -> list[str]:
def get_enabled_region_names(self) -> list[str]:
return [r.name for r in self.get_enabled_regions()]

def get_region_by_name(self, name: str) -> TAbstractBaseRegion | None:
def get_region_by_name(self, name: str) -> TBaseRegion | None:
return next((region for region in self.regions if region.name == name), None)


GenericSequence = AbstractBaseSequence[GenericRegion]
TAbstractBaseSequence = TypeVar("TAbstractBaseSequence", bound=AbstractBaseSequence)
GenericSequence = BaseSequence[GenericRegion]
TBaseSequence = TypeVar("TBaseSequence", bound=BaseSequence)
8 changes: 4 additions & 4 deletions src/dodal/devices/electron_analyser/specs/specs_region.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
from pydantic import Field

from dodal.devices.electron_analyser.base.base_region import (
AbstractBaseRegion,
AbstractBaseSequence,
BaseRegion,
BaseSequence,
TLensMode,
TPsuMode,
)
from dodal.devices.electron_analyser.specs.specs_enums import AcquisitionMode


class SpecsRegion(
AbstractBaseRegion[AcquisitionMode, TLensMode, float],
BaseRegion[AcquisitionMode, TLensMode, float],
Generic[TLensMode, TPsuMode],
):
# Override base class with defaults
Expand All @@ -32,6 +32,6 @@ class SpecsRegion(


class SpecsSequence(
AbstractBaseSequence[SpecsRegion[TLensMode, TPsuMode]], Generic[TLensMode, TPsuMode]
BaseSequence[SpecsRegion[TLensMode, TPsuMode]], Generic[TLensMode, TPsuMode]
):
regions: list[SpecsRegion[TLensMode, TPsuMode]] = Field(default_factory=lambda: [])
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import uuid
from typing import Generic, TypeVar

from ophyd_async.core import StrictEnum
from pydantic import Field, field_validator

from dodal.devices.electron_analyser.base.base_region import (
AbstractBaseRegion,
AbstractBaseSequence,
BaseRegion,
BaseSequence,
TLensMode,
TPsuMode,
)
Expand All @@ -19,7 +18,7 @@


class VGScientaRegion(
AbstractBaseRegion[AcquisitionMode, TLensMode, TPassEnergyEnum],
BaseRegion[AcquisitionMode, TLensMode, TPassEnergyEnum],
Generic[TLensMode, TPassEnergyEnum],
):
# Override defaults of base region class
Expand All @@ -32,7 +31,6 @@ class VGScientaRegion(
acquire_time: float = Field(default=1.0, alias="step_time")
energy_step: float = Field(default=200.0)
# Specific to this class
id: str = Field(default=str(uuid.uuid4()), alias="region_id")
total_steps: float = 13.0
total_time: float = 13.0
min_x: int = Field(alias="first_x_channel", default=1)
Expand All @@ -58,7 +56,7 @@ def validate_pass_energy(cls, val):


class VGScientaSequence(
AbstractBaseSequence[VGScientaRegion[TLensMode, TPassEnergyEnum]],
BaseSequence[VGScientaRegion[TLensMode, TPassEnergyEnum]],
Generic[TLensMode, TPsuMode, TPassEnergyEnum],
):
psu_mode: TPsuMode = Field(alias="element_set")
Expand Down
Loading
Loading