Skip to content

Create initial analyserscan plan #1205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 142 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
f48767a
Add classes to read sequence files into classes
oliwenmandiamond Mar 6, 2025
0865269
Added I09 epics analyser detector
oliwenmandiamond Mar 6, 2025
744665d
Created generic region class to extend from. Moved setting detector l…
oliwenmandiamond Mar 12, 2025
e5ff26f
helper functions to load json file into a class and save
oliwenmandiamond Mar 14, 2025
c28554e
Generic electron analyser device class added along with generic regio…
oliwenmandiamond Mar 14, 2025
21cca85
Added more specialised electron analyser device classes than inherit …
oliwenmandiamond Mar 14, 2025
1d40fd3
Removed __main__ testing
oliwenmandiamond Mar 14, 2025
373ec98
Update BaseRegion text
oliwenmandiamond Mar 14, 2025
8749141
Correct region names to be VGScienta
oliwenmandiamond Mar 14, 2025
5f4b1f3
Create BaseSequence class that specialised classes inherit from
oliwenmandiamond Mar 14, 2025
e96c367
Make signals for devices rw rather than just w
oliwenmandiamond Mar 14, 2025
4b672f7
Remove unused TypeVar
oliwenmandiamond Mar 14, 2025
f042a61
Added some basic tests for region classes
oliwenmandiamond Mar 14, 2025
8924ad4
Correct PV types to stop errors on connect()
oliwenmandiamond Mar 17, 2025
ac30b6c
Added more in depth tests for data region classes
oliwenmandiamond Mar 17, 2025
922f293
Removed analyser classes
oliwenmandiamond Mar 17, 2025
99bc973
Add analyser classes
oliwenmandiamond Mar 18, 2025
7aea2de
Made VGScientaAnalyser compatible with bps.abs_set plan
oliwenmandiamond Mar 18, 2025
13f977c
Made SpecsAnalyser compatible with bps.abs_set plan
oliwenmandiamond Mar 18, 2025
fe4cbed
Corrected BaseRegion.get_enabled_regions() type
oliwenmandiamond Mar 19, 2025
940615c
Corrected VGScientaRegion.get_excitation_energy_source_by_region()
oliwenmandiamond Mar 19, 2025
f06ebb0
Added basic analyser tests and common util for testing
oliwenmandiamond Mar 19, 2025
fb5e806
Removed specs and vgscienta folder, more modular tests
oliwenmandiamond Mar 20, 2025
61808a8
Added helper functions to region classes and corrected typing syntax
oliwenmandiamond Mar 20, 2025
b277afe
Improved tests for region classes
oliwenmandiamond Mar 20, 2025
dfbe669
Analyser code now uses region helper functions
oliwenmandiamond Mar 20, 2025
85e1480
Cleaned up code by applying commits in PR
oliwenmandiamond Mar 20, 2025
367c1ed
Added VGScientaAnalyser tests
oliwenmandiamond Mar 20, 2025
7eabbfe
Merge branch 'main' into add_analyser
oliwenmandiamond Mar 24, 2025
f9f4ac2
Fixed paramitised tests for analyser
oliwenmandiamond Mar 24, 2025
67fc331
Added full tests for specs
oliwenmandiamond Mar 24, 2025
3de059f
Removed duplicate classes and tests after rebase
oliwenmandiamond Mar 24, 2025
cec0dd3
excitation energy is a madatory arg
oliwenmandiamond Mar 24, 2025
1c9583a
Refactored tests into abstract classes to reduce duplicate tests betw…
oliwenmandiamond Mar 25, 2025
16d1fcb
Fix for unit tests not passing
oliwenmandiamond Mar 25, 2025
4528fc5
Merge branch 'main' into add_analyser
DominicOram Mar 25, 2025
21c25e8
Removed unused base region class
oliwenmandiamond Mar 26, 2025
6351489
Renamed analyser classes to analyser controllers for clarify
oliwenmandiamond Mar 26, 2025
186054e
correct analyser test import after name change
oliwenmandiamond Mar 26, 2025
c6cea13
Removed beamline implementation as will be committed in different PR
oliwenmandiamond Mar 26, 2025
68975ce
Removed I09 as will be added in different PR
oliwenmandiamond Mar 26, 2025
60b38be
Updated classes to assume region.energyStep is eV and passEnergy is str
oliwenmandiamond Mar 26, 2025
224bcde
Merge branch 'main' into add_analyser
oliwenmandiamond Mar 26, 2025
ea59401
Fixed unit tests by adding __init__.py
oliwenmandiamond Mar 26, 2025
309c4fd
Removed reference to pass energy type
oliwenmandiamond Mar 27, 2025
e3ecde5
Corrected .gitignore to ignore Pipfile
oliwenmandiamond Mar 27, 2025
3e96f37
Converted classes to use snake_case
oliwenmandiamond Mar 27, 2025
8bbd061
Moved analyser logic to plans, flattened tests
oliwenmandiamond Mar 28, 2025
95c9168
Added analyser_controller to i09, i09-1, p60, b07, b07-1
oliwenmandiamond Mar 31, 2025
3ff5f0d
Added generic data that can be read
oliwenmandiamond Mar 31, 2025
36015a7
Remove reference to Abstract regions from analyser as logic moved to …
oliwenmandiamond Mar 31, 2025
4dcf381
Add energy and angle axis to specs
oliwenmandiamond Mar 31, 2025
754201c
switched round arguments for energy conversion functions
oliwenmandiamond Apr 1, 2025
015176b
Remove reference to Abstract regions from analyser as logic moved to …
oliwenmandiamond Mar 31, 2025
c8a995a
Added generic data that can be read
oliwenmandiamond Mar 31, 2025
a3698d7
Adding reading analyser devices to tests
oliwenmandiamond Apr 1, 2025
065fb6e
Adding reading analyser devices to tests
oliwenmandiamond Apr 1, 2025
9d646ae
Added generic data that can be read
oliwenmandiamond Mar 31, 2025
ee79fb5
Add energy and angle axis to specs
oliwenmandiamond Mar 31, 2025
713ab0a
Added MsgGenerator to analyser plans
oliwenmandiamond Apr 2, 2025
2f596c5
Added binding energy axis
oliwenmandiamond Apr 2, 2025
3482d3d
Made energy_axis and angle_axis signals inside abstract controller
oliwenmandiamond Apr 2, 2025
47f9112
Added AdBaseIO to abstract analyser class
oliwenmandiamond Apr 2, 2025
10ac118
Merge branch 'main' into 1151_add_analyser_controller_devices_to_beam…
oliwenmandiamond Apr 7, 2025
238ab62
Renamed controllers to be driver io
oliwenmandiamond Apr 7, 2025
e7d0f8f
Renamed analyser_controller for beamlines to analyser_driver
oliwenmandiamond Apr 7, 2025
925b557
Fix liniting issues
oliwenmandiamond Apr 7, 2025
66e8130
Renamed analyser controller to driver io
oliwenmandiamond Apr 7, 2025
b11c3de
Update oav pv on i19-1 (#1146)
noemifrisina Mar 28, 2025
bd8a470
Ban time.sleep, exempting tests (#1134)
jacob720 Mar 28, 2025
340b9b9
Update to latest ophyd async (#1143)
DominicOram Mar 28, 2025
40d9bb0
Add beamstop to I19 (#1071)
noemifrisina Mar 31, 2025
77685f6
Add baton device (#1144)
DominicOram Mar 31, 2025
5e39f50
Add XYZ positioner as sample stage to ViSR (#1127)
stan-dot Apr 1, 2025
d806f41
Implement a new access controlled shutter for i19 (#1135)
noemifrisina Apr 1, 2025
c358c41
Get fixes from beamline testing and fix unit tests (#1160)
noemifrisina Apr 5, 2025
e6fc5b7
Removed all references to EpicsMotor, replacing with Motor where appr…
adaudon Apr 5, 2025
b63bc03
Add analyser classes (#1125)
oliwenmandiamond Apr 7, 2025
d032134
Renamed controllers to be driver io
oliwenmandiamond Apr 7, 2025
ea04c56
Fix liniting issues
oliwenmandiamond Apr 7, 2025
33b75b2
Added generic data that can be read
oliwenmandiamond Mar 31, 2025
9f3c2de
Add energy and angle axis to specs
oliwenmandiamond Mar 31, 2025
21d7000
Added generic data that can be read
oliwenmandiamond Mar 31, 2025
a3edfc5
Adding reading analyser devices to tests
oliwenmandiamond Apr 1, 2025
96c3e86
Added generic data that can be read
oliwenmandiamond Mar 31, 2025
f4e1986
Add energy and angle axis to specs
oliwenmandiamond Mar 31, 2025
c1af16f
Added binding energy axis
oliwenmandiamond Apr 2, 2025
293a92e
Made energy_axis and angle_axis signals inside abstract controller
oliwenmandiamond Apr 2, 2025
79b2e64
Added AdBaseIO to abstract analyser class
oliwenmandiamond Apr 2, 2025
6d567c7
Renamed analyser controller to driver io
oliwenmandiamond Apr 7, 2025
14a40e9
Fix tests
oliwenmandiamond Apr 7, 2025
6c2d9ca
Merge branch '1151_add_analyser_controller_devices_to_beamline' into …
oliwenmandiamond Apr 7, 2025
b5beff8
Remove duplicate analyer class left over from rebase
oliwenmandiamond Apr 7, 2025
bc236f5
Fixed step time pv value
oliwenmandiamond Apr 7, 2025
74b633c
Changed EnergyMode to be a StrictEnum
oliwenmandiamond Apr 11, 2025
aef8a1f
Correct import for ADImageMode
oliwenmandiamond Apr 15, 2025
00f89fe
Corrected energy step to be in eV rather than meV
oliwenmandiamond Apr 15, 2025
cb58bc6
Added different pass energy type in driver as cannot be resolved in e…
oliwenmandiamond Apr 15, 2025
d9eefa3
Added synchrotron to relavent beamlines. Removed name from device_fac…
oliwenmandiamond Apr 15, 2025
54012c0
Removed pass on abstract method and gave docstring
oliwenmandiamond Apr 15, 2025
cd3ffc2
make turbo slit movable (#1126)
stan-dot Apr 8, 2025
99d5f66
Add i23 goniometer (#1163)
DominicOram Apr 8, 2025
caab011
Remove start document path provider (#1170)
abbiemery Apr 10, 2025
05e818d
Create a common base device that sends requests to blueapi on I19 opt…
noemifrisina Apr 11, 2025
d425d06
Allow interpolation in undulator gap (#1155)
DominicOram Apr 15, 2025
3f90aa4
949 make ophyd devices for the diagonstics for i10 (#960)
Relm-Arrowny Apr 15, 2025
00f6915
(mx-bluesky#719) Beam centre lookup table should support linear extra…
rtuck99 Apr 15, 2025
01b47f1
Renamed configure_controller plan to configure_driver
oliwenmandiamond Apr 16, 2025
a2a67e7
Added analyser_controller to i09, i09-1, p60, b07, b07-1 (#1152)
oliwenmandiamond Apr 16, 2025
c254196
Merge remote-tracking branch 'origin/main' into get_detector_data_fro…
oliwenmandiamond Apr 16, 2025
8ccf706
Made abstract analyser class inherit from AdBaseIO
oliwenmandiamond Apr 16, 2025
e2cb03d
Added name, excitation_energy, energy_mode, and binding_energy_axis a…
oliwenmandiamond Apr 17, 2025
1bf7ff4
Added tests for new signals
oliwenmandiamond Apr 17, 2025
90153d5
Added binding energy test
oliwenmandiamond Apr 17, 2025
1d75872
Added additional comments to signals
oliwenmandiamond Apr 17, 2025
0c30fd7
Add plan decorator
oliwenmandiamond Apr 17, 2025
2b3eafe
Fixed dodal connect errors
oliwenmandiamond Apr 17, 2025
355bf33
Added classes / functions to __init__. Redone imports
oliwenmandiamond Apr 24, 2025
a83fde9
Remove duplicate name from device_facotry functions
oliwenmandiamond Apr 24, 2025
fc9a25e
Make BaseDCM (#1111)
olliesilvester Apr 17, 2025
f4e4a24
Add shutter for i23 (#1169)
DominicOram Apr 23, 2025
26efb0e
Update turbo_slit.py with units inside a comment (#1167)
stan-dot Apr 23, 2025
e53c477
Merge branch 'main' into get_detector_data_from_analyser_controller
Relm-Arrowny Apr 25, 2025
d2ffd52
fix circular import issue
oliwenmandiamond Apr 25, 2025
73a1466
Revert device_factory formatting
oliwenmandiamond Apr 25, 2025
eda0720
Converted create_r_hardware_backed_soft_signals to derived_signal_r
oliwenmandiamond Apr 25, 2025
e781a21
Added tests for specs energy and angle axis
oliwenmandiamond Apr 29, 2025
5731fb9
Separated out read and read_configuration signals
oliwenmandiamond Apr 30, 2025
289b1c9
Swapped round if statement
oliwenmandiamond Apr 30, 2025
e3fe390
Added snapshot_values to tests
oliwenmandiamond Apr 30, 2025
71ddf73
Added analyser detectors, renamed _io from analysers
oliwenmandiamond Apr 15, 2025
78de39b
Added analyserscan plan
oliwenmandiamond Apr 15, 2025
bec9eaf
Removed list of per point and per scan values, added get_sequence met…
oliwenmandiamond Apr 30, 2025
cddef83
Update MsgGenerator import
oliwenmandiamond May 1, 2025
16ae772
Merge branch 'main' into create_initial_analyserscan_plan
oliwenmandiamond May 1, 2025
b157c86
Changed get_signals to create_signals
oliwenmandiamond May 1, 2025
6039fc3
Reverted formatting of create_signal functions from merge
oliwenmandiamond May 1, 2025
bc6da1f
Remove unused comments
oliwenmandiamond May 1, 2025
70bec15
Add detector to package
oliwenmandiamond May 2, 2025
c1ac1aa
Remove analyserscan
oliwenmandiamond May 2, 2025
38483bc
Improve error message load_json_file_to_class
oliwenmandiamond May 2, 2025
beace20
Created detector module, renamed others back to _Io.py
oliwenmandiamond May 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/dodal/common/data_util.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from typing import TypeVar

from pydantic import BaseModel
Expand All @@ -9,6 +10,9 @@
t: type[TBaseModel],
file: str,
) -> TBaseModel:
if not os.path.isfile(file):
raise FileNotFoundError(f"Cannot find file {file}")

Check warning on line 14 in src/dodal/common/data_util.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/common/data_util.py#L14

Added line #L14 was not covered by tests

with open(file) as f:
json_obj = f.read()
cls = t.model_validate_json(json_obj)
Expand Down
3 changes: 3 additions & 0 deletions src/dodal/devices/electron_analyser/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .abstract_region import EnergyMode
from .detector import ElectronAnalyserDetector, ElectronAnalyserRegionDetector
from .specs_analyser_io import SpecsAnalyserDriverIO
from .specs_region import SpecsRegion, SpecsSequence
from .util import to_binding_energy, to_kinetic_energy
Expand All @@ -11,6 +12,8 @@

__all__ = [
"EnergyMode",
"ElectronAnalyserDetector",
"ElectronAnalyserRegionDetector",
"SpecsAnalyserDriverIO",
"SpecsRegion",
"SpecsSequence",
Expand Down
67 changes: 35 additions & 32 deletions src/dodal/devices/electron_analyser/abstract_analyser_io.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from abc import ABC, abstractmethod
from typing import TypeVar
from typing import Generic, TypeVar

import numpy as np
from ophyd_async.core import (
Array1D,
Reference,
SignalR,
StandardReadable,
StandardReadableFormat,
Expand All @@ -13,17 +14,32 @@
from ophyd_async.epics.adcore import ADBaseIO
from ophyd_async.epics.core import epics_signal_r, epics_signal_rw

from dodal.devices.electron_analyser.abstract_region import EnergyMode
from dodal.devices.electron_analyser.abstract_region import (
EnergyMode,
TAbstractBaseSequence,
)
from dodal.devices.electron_analyser.util import to_binding_energy


class AbstractAnalyserDriverIO(ABC, StandardReadable, ADBaseIO):
class AbstractAnalyserDriverIO(
ABC, StandardReadable, ADBaseIO, Generic[TAbstractBaseSequence]
):
"""
Generic device to configure electron analyser with new region settings.
Electron analysers should inherit from this class for further specialisation.
"""

def __init__(self, prefix: str, name: str = "") -> None:
with self.add_children_as_readables():
self.image = Reference(
epics_signal_r(Array1D[np.float64], prefix + "IMAGE")
)
self.spectrum = epics_signal_r(Array1D[np.float64], prefix + "INT_SPECTRUM")
self.total_intensity = derived_signal_r(
self._calculate_total_intensity, spectrum=self.spectrum
)
self.excitation_energy = soft_signal_rw(float, initial_value=0, units="eV")

with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
# Used for setting up region data acquisition.
self.region_name = soft_signal_rw(str, initial_value="null")
Expand All @@ -40,28 +56,8 @@
self.energy_step = epics_signal_rw(float, prefix + "STEP_SIZE")
self.iterations = epics_signal_rw(int, prefix + "NumExposures")
self.acquisition_mode = epics_signal_rw(str, prefix + "ACQ_MODE")
self.step_time = epics_signal_r(float, prefix + "AcquireTime")

self.total_steps = self._create_total_steps_signal(prefix)
self.total_time = derived_signal_r(
self._calculate_total_time,
"s",
total_steps=self.total_steps,
step_time=self.step_time,
iterations=self.iterations,
)

with self.add_children_as_readables():
self.image = epics_signal_r(Array1D[np.float64], prefix + "IMAGE")
self.spectrum = epics_signal_r(Array1D[np.float64], prefix + "INT_SPECTRUM")
self.total_intensity = derived_signal_r(
self._calculate_total_intensity, spectrum=self.spectrum
)
# ToDo - Ideally the below are only collected once per region. However, they
# need to be read after the first point of a region (stream). Bluesky /
# ophyd currently doesn't support this and therefore must be read per point
# as a workaround, otherwise they return the previous region values.
self.excitation_energy = soft_signal_rw(float, initial_value=0, units="eV")
# Read once per scan after data acquired
self.energy_axis = self._create_energy_axis_signal(prefix)
self.binding_energy_axis = derived_signal_r(
self._calculate_binding_energy_axis,
Expand All @@ -71,6 +67,16 @@
energy_mode=self.energy_mode,
)
self.angle_axis = self._create_angle_axis_signal(prefix)
self.step_time = epics_signal_r(float, prefix + "AcquireTime")
self.total_steps = epics_signal_r(int, prefix + "TOTAL_POINTS_RBV")
self.total_time = derived_signal_r(
self._calculate_total_time,
"s",
total_steps=self.total_steps,
step_time=self.step_time,
iterations=self.iterations,
)

super().__init__(prefix=prefix, name=name)

@abstractmethod
Expand Down Expand Up @@ -101,20 +107,13 @@
]
)

@abstractmethod
def _create_total_steps_signal(self, prefix: str) -> SignalR[int]:
"""
The signal that defines the total steps. Depends if analyser knows this
information before the first point.
"""

def _calculate_total_time(
self, total_steps: int, step_time: float, iterations: int
) -> float:
return total_steps * step_time * iterations

def _calculate_total_intensity(self, spectrum: Array1D[np.float64]) -> float:
return float(np.sum(spectrum))
return float(np.sum(spectrum, dtype=np.float64))

@property
@abstractmethod
Expand All @@ -124,6 +123,10 @@
for the underlying analyser software and cannot be changed on epics side.
"""

@abstractmethod
def sequence_type(self) -> type[TAbstractBaseSequence]:
pass

Check warning on line 128 in src/dodal/devices/electron_analyser/abstract_analyser_io.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/abstract_analyser_io.py#L128

Added line #L128 was not covered by tests


TAbstractAnalyserDriverIO = TypeVar(
"TAbstractAnalyserDriverIO", bound=AbstractAnalyserDriverIO
Expand Down
185 changes: 185 additions & 0 deletions src/dodal/devices/electron_analyser/detector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import asyncio
from typing import Generic

from bluesky.protocols import (
Reading,
Stageable,
Triggerable,
)
from event_model import DataKey
from ophyd_async.core import (
DEFAULT_TIMEOUT,
AsyncStatus,
Device,
Reference,
set_and_wait_for_value,
)
from ophyd_async.core._protocol import AsyncConfigurable, AsyncReadable
from ophyd_async.epics.adcore import (
DEFAULT_GOOD_STATES,
ADState,
stop_busy_record,
)

from dodal.common.data_util import load_json_file_to_class
from dodal.devices.electron_analyser.abstract_analyser_io import (
AbstractAnalyserDriverIO,
)
from dodal.devices.electron_analyser.abstract_region import (
TAbstractBaseRegion,
TAbstractBaseSequence,
)


class AnalyserController:
def __init__(
self,
driver: AbstractAnalyserDriverIO,
good_states: frozenset[ADState] = DEFAULT_GOOD_STATES,
) -> None:
self.driver = driver
self.good_states = good_states
self.frame_timeout = DEFAULT_TIMEOUT
self._arm_status: AsyncStatus | None = None

async def arm(self):
self._arm_status = await self.start_acquiring_driver_and_ensure_status()

Check warning on line 46 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L46

Added line #L46 was not covered by tests

async def disarm(self):
# We can't use caput callback as we already used it in arm() and we can't have
# 2 or they will deadlock
await stop_busy_record(self.driver.acquire, False, timeout=1)

Check warning on line 51 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L51

Added line #L51 was not covered by tests

async def start_acquiring_driver_and_ensure_status(self) -> AsyncStatus:
"""Start acquiring driver, raising ValueError if the detector is in a bad state.
This sets driver.acquire to True, and waits for it to be True up to a timeout.
Then, it checks that the DetectorState PV is in DEFAULT_GOOD_STATES,
and otherwise raises a ValueError.
:returns AsyncStatus:
An AsyncStatus that can be awaited to set driver.acquire to True and perform
subsequent raising (if applicable) due to detector state.
"""
status = await set_and_wait_for_value(

Check warning on line 62 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L62

Added line #L62 was not covered by tests
self.driver.acquire,
True,
timeout=DEFAULT_TIMEOUT,
wait_for_set_completion=False,
)

async def complete_acquisition() -> None:

Check warning on line 69 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L69

Added line #L69 was not covered by tests
# NOTE: possible race condition here between the callback from
# set_and_wait_for_value and the detector state updating.
await status
state = await self.driver.detector_state.get_value()
if state not in self.good_states:
raise ValueError(

Check warning on line 75 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L72-L75

Added lines #L72 - L75 were not covered by tests
f"Final detector state {state.value} not "
"in valid end states: {self.good_states}"
)

return AsyncStatus(complete_acquisition())

Check warning on line 80 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L80

Added line #L80 was not covered by tests

async def wait_for_idle(self):
if self._arm_status and not self._arm_status.done:
await self._arm_status
self._arm_status = None

Check warning on line 85 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L83-L85

Added lines #L83 - L85 were not covered by tests


class BaseElectronAnalyserDetector(
Device,
Stageable,
Triggerable,
AsyncReadable,
AsyncConfigurable,
):
"""
Detector for data acquisition of electron analyser. Can only acquire using settings
already configured for the device.
"""

def __init__(
self,
name: str,
driver: AbstractAnalyserDriverIO,
):
self.driver_ref: Reference[AbstractAnalyserDriverIO] = Reference(driver)
self.controller: AnalyserController = AnalyserController(driver=driver)
super().__init__(name)

@AsyncStatus.wrap
async def trigger(self) -> None:
await self.controller.arm()
await self.controller.wait_for_idle()

Check warning on line 112 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L111-L112

Added lines #L111 - L112 were not covered by tests

@AsyncStatus.wrap
async def stage(self) -> None:
"""Make sure the detector is idle and ready to be used."""
await asyncio.gather(self.controller.disarm())

Check warning on line 117 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L117

Added line #L117 was not covered by tests

@AsyncStatus.wrap
async def unstage(self) -> None:
"""Disarm the detector."""
await asyncio.gather(self.controller.disarm())

Check warning on line 122 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L122

Added line #L122 was not covered by tests

async def read(self) -> dict[str, Reading]:
return await self.driver_ref().read()

Check warning on line 125 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L125

Added line #L125 was not covered by tests

async def describe(self) -> dict[str, DataKey]:
data = await self.driver_ref().describe()

Check warning on line 128 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L128

Added line #L128 was not covered by tests
# Correct the shape for image
prefix = self.driver_ref().name + "-"
energy_size = len(await self.driver_ref().energy_axis.get_value())
angle_size = len(await self.driver_ref().angle_axis.get_value())
data[prefix + "image"]["shape"] = [angle_size, energy_size]
return data

Check warning on line 134 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L130-L134

Added lines #L130 - L134 were not covered by tests

async def read_configuration(self) -> dict[str, Reading]:
return await self.driver_ref().read_configuration()

Check warning on line 137 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L137

Added line #L137 was not covered by tests

async def describe_configuration(self) -> dict[str, DataKey]:
return await self.driver_ref().describe_configuration()

Check warning on line 140 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L140

Added line #L140 was not covered by tests


class ElectronAnalyserRegionDetector(
BaseElectronAnalyserDetector,
Stageable,
Generic[TAbstractBaseRegion],
):
"""
Extends electron analyser detector to configure specific region settings before data
acqusition.
"""

def __init__(
self, name: str, driver: AbstractAnalyserDriverIO, region: TAbstractBaseRegion
):
super().__init__(name, driver)
self.region = region

@AsyncStatus.wrap
async def stage(self) -> None:
super().stage()

Check warning on line 161 in src/dodal/devices/electron_analyser/detector.py

View check run for this annotation

Codecov / codecov/patch

src/dodal/devices/electron_analyser/detector.py#L161

Added line #L161 was not covered by tests
# configure region here


class ElectronAnalyserDetector(
BaseElectronAnalyserDetector,
Generic[TAbstractBaseSequence, TAbstractBaseRegion],
):
"""
Electron analyser detector with the additional functionality to load a sequence file
and create a list of ElectronAnalyserRegionDetector objects. These will setup
configured region settings before data acquisition.
"""

def load_sequence(self, filename: str) -> TAbstractBaseSequence:
return load_json_file_to_class(self.driver_ref().sequence_type(), filename)

def create_region_detectors(
self, filename: str
) -> list[ElectronAnalyserRegionDetector[TAbstractBaseRegion]]:
seq = self.load_sequence(filename)
return [
ElectronAnalyserRegionDetector(self.name, self.driver_ref(), region)
for region in seq.get_enabled_regions()
]
9 changes: 5 additions & 4 deletions src/dodal/devices/electron_analyser/specs_analyser_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
from dodal.devices.electron_analyser.abstract_analyser_io import (
AbstractAnalyserDriverIO,
)
from dodal.devices.electron_analyser.specs_region import SpecsSequence


class SpecsAnalyserDriverIO(AbstractAnalyserDriverIO):
class SpecsAnalyserDriverIO(AbstractAnalyserDriverIO[SpecsSequence]):
def __init__(self, prefix: str, name: str = "") -> None:
with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
# Used for setting up region data acquisition.
Expand Down Expand Up @@ -59,9 +60,9 @@ def _calculate_energy_axis(
axis = np.array([min_energy + i * step for i in range(total_points_iterations)])
return axis

def _create_total_steps_signal(self, prefix: str) -> SignalR[int]:
return epics_signal_r(int, prefix + "TOTAL_POINTS_RBV")

@property
def pass_energy_type(self) -> type:
return float

def sequence_type(self) -> type[SpecsSequence]:
return SpecsSequence
Loading
Loading