diff --git a/src/ibex_bluesky_core/demo_plan.py b/src/ibex_bluesky_core/demo_plan.py index 41b1c2f..e567347 100644 --- a/src/ibex_bluesky_core/demo_plan.py +++ b/src/ibex_bluesky_core/demo_plan.py @@ -10,7 +10,7 @@ from ibex_bluesky_core.devices import get_pv_prefix from ibex_bluesky_core.devices.block import BlockRwRbv, block_rw_rbv -from ibex_bluesky_core.devices.dae import Dae +from ibex_bluesky_core.devices.dae.dae import Dae from ibex_bluesky_core.run_engine import get_run_engine __all__ = ["run_demo_plan", "demo_plan"] diff --git a/src/ibex_bluesky_core/devices/__init__.py b/src/ibex_bluesky_core/devices/__init__.py index dc6acfe..9c8770b 100644 --- a/src/ibex_bluesky_core/devices/__init__.py +++ b/src/ibex_bluesky_core/devices/__init__.py @@ -34,7 +34,7 @@ def dehex_and_decompress(value: bytes) -> bytes: def compress_and_hex(value: str) -> bytes: - """Compresses the inputted string and encodes it as hex. + """Compress the inputted string and encode it as hex. Args: value: The string to be compressed @@ -47,8 +47,9 @@ def compress_and_hex(value: str) -> bytes: def isis_epics_signal_rw(datatype: Type[T], read_pv: str, name: str = "") -> SignalRW[T]: - """Utility function for making a RW signal using the ISIS PV naming standard ie. read_pv being TITLE, - write_pv being TITLE:SP + """Utility function to make a RW signal using the ISIS PV naming standard + ie. read_pv being TITLE, write_pv being TITLE:SP. + """ write_pv = f"{read_pv}:SP" return epics_signal_rw(datatype, read_pv, write_pv, name) diff --git a/src/ibex_bluesky_core/devices/dae/__init__.py b/src/ibex_bluesky_core/devices/dae/__init__.py index d4a4eef..72a0368 100644 --- a/src/ibex_bluesky_core/devices/dae/__init__.py +++ b/src/ibex_bluesky_core/devices/dae/__init__.py @@ -6,9 +6,7 @@ def convert_xml_to_names_and_values(xml: Element) -> Dict[str, str]: - """ - Converts an XML element's children to a dict containing .text:.text - """ + """Converts an XML element's children to a dict containing .text:.text.""" names_and_values = dict() elements = get_all_elements_in_xml_with_child_called_name(xml) for element in elements: @@ -18,8 +16,8 @@ def convert_xml_to_names_and_values(xml: Element) -> Dict[str, str]: return names_and_values -def get_all_elements_in_xml_with_child_called_name(xml: Element): - # This finds all elements with a "name" element, but ignores the first one as it's the root +def get_all_elements_in_xml_with_child_called_name(xml: Element) -> List[Element]: + """Find all elements with a "name" element, but ignore the first one as it's the root.""" elements = xml.findall(".//Name/..")[1:] return elements @@ -33,8 +31,10 @@ def _get_names_and_values(element: Element) -> tuple[Any, Any] | tuple[None, Non return None, None -def set_value_in_dae_xml(elements: List[Element], name: str, value: Any): - """Finds and sets a value in the DAE XML, given a name and value. Does nothing (by design) if value is None as to leave value unchanged""" +def set_value_in_dae_xml(elements: List[Element], name: str, value: str|Enum|int) -> None: + """Find and set a value in the DAE XML, given a name and value. + Do nothing (by design) if value is None to leave value unchanged. + """ if value is not None: if isinstance(value, Enum): value = value.value diff --git a/src/ibex_bluesky_core/devices/dae/dae.py b/src/ibex_bluesky_core/devices/dae/dae.py index 5d92189..4bebe15 100644 --- a/src/ibex_bluesky_core/devices/dae/dae.py +++ b/src/ibex_bluesky_core/devices/dae/dae.py @@ -3,6 +3,7 @@ import asyncio from enum import Enum +import numpy as np from bluesky.protocols import Triggerable from ophyd_async.core import AsyncStatus, SignalR, SignalRW, StandardReadable from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw @@ -15,10 +16,8 @@ from ibex_bluesky_core.devices.dae.dae_period_settings import DaePeriodSettings from ibex_bluesky_core.devices.dae.dae_settings import DaeSettings from ibex_bluesky_core.devices.dae.dae_tcb_settings import DaeTCBSettings - from src.ibex_bluesky_core.devices.dae.dae_spectra import DaeSpectra -import numpy as np class RunstateEnum(str, Enum): PROCESSING = "PROCESSING" @@ -37,7 +36,7 @@ class RunstateEnum(str, Enum): STORING = "STORING" CHANGING = "CHANGING" - def __str__(self): + def __str__(self) -> str: return str(self.value) diff --git a/src/ibex_bluesky_core/devices/dae/dae_controls.py b/src/ibex_bluesky_core/devices/dae/dae_controls.py index d6c9229..9a84b86 100644 --- a/src/ibex_bluesky_core/devices/dae/dae_controls.py +++ b/src/ibex_bluesky_core/devices/dae/dae_controls.py @@ -6,7 +6,7 @@ class DaeControls(StandardReadable): - def __init__(self, dae_prefix, name=""): + def __init__(self, dae_prefix: str, name: str = "") -> None: self.begin_run: SignalX = epics_signal_x(f"{dae_prefix}BEGINRUN") self.begin_run_ex: BeginRunEx = BeginRunEx(dae_prefix) self.end_run: SignalX = epics_signal_x(f"{dae_prefix}ENDRUN") @@ -26,7 +26,7 @@ class BeginRunExBits(IntFlag): class BeginRunEx(StandardReadable, Movable): - def __init__(self, dae_prefix, name=""): + def __init__(self, dae_prefix: str, name: str = "") -> None: self.begin_run_ex: SignalRW = epics_signal_rw(int, f"{dae_prefix}BEGINRUNEX") super().__init__(name=name) diff --git a/src/ibex_bluesky_core/devices/dae/dae_event_mode.py b/src/ibex_bluesky_core/devices/dae/dae_event_mode.py index b260503..807ad5f 100644 --- a/src/ibex_bluesky_core/devices/dae/dae_event_mode.py +++ b/src/ibex_bluesky_core/devices/dae/dae_event_mode.py @@ -3,7 +3,7 @@ class DaeEventMode(StandardReadable): - def __init__(self, dae_prefix, name=""): + def __init__(self, dae_prefix: str, name: str = "") -> None: with self.add_children_as_readables(): self.fraction: SignalR[float] = epics_signal_r(float, f"{dae_prefix}EVENTMODEFRACTION") self.buf_used: SignalR[float] = epics_signal_r(float, f"{dae_prefix}EVENTMODEBUFUSED") diff --git a/src/ibex_bluesky_core/devices/dae/dae_monitor.py b/src/ibex_bluesky_core/devices/dae/dae_monitor.py index 2985561..78bc3ad 100644 --- a/src/ibex_bluesky_core/devices/dae/dae_monitor.py +++ b/src/ibex_bluesky_core/devices/dae/dae_monitor.py @@ -3,7 +3,7 @@ class DaeMonitor(StandardReadable): - def __init__(self, dae_prefix, name=""): + def __init__(self, dae_prefix: str, name: str = "") -> None: with self.add_children_as_readables(): self.spectrum: SignalR[int] = epics_signal_r(int, f"{dae_prefix}MONITORCOUNTS") self.counts: SignalR[int] = epics_signal_r(int, f"{dae_prefix}MONITORSPECTRUM") diff --git a/src/ibex_bluesky_core/devices/dae/dae_period.py b/src/ibex_bluesky_core/devices/dae/dae_period.py index 00fe0ff..b00c814 100644 --- a/src/ibex_bluesky_core/devices/dae/dae_period.py +++ b/src/ibex_bluesky_core/devices/dae/dae_period.py @@ -3,7 +3,7 @@ class DaePeriod(StandardReadable): - def __init__(self, dae_prefix, name=""): + def __init__(self, dae_prefix: str, name: str = "") -> None: with self.add_children_as_readables(): self.run_duration: SignalR[int] = epics_signal_r(int, f"{dae_prefix}RUNDURATION_PD") self.good_frames: SignalR[int] = epics_signal_r(int, f"{dae_prefix}GOODFRAMES_PD") diff --git a/src/ibex_bluesky_core/devices/dae/dae_period_settings.py b/src/ibex_bluesky_core/devices/dae/dae_period_settings.py index a6d4a89..8a999b5 100644 --- a/src/ibex_bluesky_core/devices/dae/dae_period_settings.py +++ b/src/ibex_bluesky_core/devices/dae/dae_period_settings.py @@ -1,5 +1,5 @@ import xml.etree.ElementTree as ET -from dataclasses import dataclass, field +from dataclasses import dataclass from enum import Enum from typing import List from xml.etree.ElementTree import tostring @@ -54,7 +54,7 @@ class DaePeriodSettingsData: periods_settings: List[SinglePeriodSettings] | None = None -def convert_xml_to_period_settings(value: str) -> DaePeriodSettingsData: +def _convert_xml_to_period_settings(value: str) -> DaePeriodSettingsData: root = ET.fromstring(value) settings_from_xml = convert_xml_to_names_and_values(root) return DaePeriodSettingsData( @@ -76,7 +76,7 @@ def convert_xml_to_period_settings(value: str) -> DaePeriodSettingsData: ) -def convert_period_settings_to_xml(current_xml: str, value: DaePeriodSettingsData) -> str: +def _convert_period_settings_to_xml(current_xml: str, value: DaePeriodSettingsData) -> str: # get xml here, then substitute values from the dataclasses root = ET.fromstring(current_xml) elements = get_all_elements_in_xml_with_child_called_name(root) @@ -95,7 +95,7 @@ def convert_period_settings_to_xml(current_xml: str, value: DaePeriodSettingsDat class DaePeriodSettings(Device, Locatable): - def __init__(self, dae_prefix, name=""): + def __init__(self, dae_prefix:str, name:str="") -> None: self.period_settings: SignalRW[str] = isis_epics_signal_rw( str, f"{dae_prefix}HARDWAREPERIODS" ) @@ -103,11 +103,11 @@ def __init__(self, dae_prefix, name=""): async def locate(self) -> Location: value = await self.period_settings.get_value() - period_settings = convert_xml_to_period_settings(value) + period_settings = _convert_xml_to_period_settings(value) return {"setpoint": period_settings, "readback": period_settings} @AsyncStatus.wrap async def set(self, value: DaePeriodSettingsData) -> None: current_xml = await self.period_settings.get_value() - to_write = convert_period_settings_to_xml(current_xml, value) + to_write = _convert_period_settings_to_xml(current_xml, value) await self.period_settings.set(to_write, wait=True) diff --git a/src/ibex_bluesky_core/devices/dae/dae_settings.py b/src/ibex_bluesky_core/devices/dae/dae_settings.py index 8bfe7ae..4af0264 100644 --- a/src/ibex_bluesky_core/devices/dae/dae_settings.py +++ b/src/ibex_bluesky_core/devices/dae/dae_settings.py @@ -78,7 +78,7 @@ class DaeSettingsData: veto_3_name: str | None = None -def convert_xml_to_dae_settings(value: str) -> DaeSettingsData: +def _convert_xml_to_dae_settings(value: str) -> DaeSettingsData: root = ET.fromstring(value) settings_from_xml = convert_xml_to_names_and_values(root) return DaeSettingsData( @@ -108,7 +108,7 @@ def convert_xml_to_dae_settings(value: str) -> DaeSettingsData: ) -def convert_dae_settings_to_xml(current_xml: str, settings: DaeSettingsData) -> str: +def _convert_dae_settings_to_xml(current_xml: str, settings: DaeSettingsData) -> str: root = ET.fromstring(current_xml) elements = get_all_elements_in_xml_with_child_called_name(root) set_value_in_dae_xml(elements, WIRING_TABLE, settings.wiring_filepath) @@ -138,17 +138,17 @@ def convert_dae_settings_to_xml(current_xml: str, settings: DaeSettingsData) -> class DaeSettings(Device, Locatable, Movable): - def __init__(self, dae_prefix, name=""): + def __init__(self, dae_prefix: str, name: str = "") -> None: self.dae_settings: SignalRW[str] = isis_epics_signal_rw(str, f"{dae_prefix}DAESETTINGS") super().__init__(name=name) async def locate(self) -> Location: value = await self.dae_settings.get_value() - period_settings = convert_xml_to_dae_settings(value) + period_settings = _convert_xml_to_dae_settings(value) return {"setpoint": period_settings, "readback": period_settings} @AsyncStatus.wrap async def set(self, value: DaeSettingsData) -> None: current_xml = await self.dae_settings.get_value() - to_write = convert_dae_settings_to_xml(current_xml, value) + to_write = _convert_dae_settings_to_xml(current_xml, value) await self.dae_settings.set(to_write, wait=True) diff --git a/src/ibex_bluesky_core/devices/dae/dae_spectra.py b/src/ibex_bluesky_core/devices/dae/dae_spectra.py index 61895e2..af62721 100644 --- a/src/ibex_bluesky_core/devices/dae/dae_spectra.py +++ b/src/ibex_bluesky_core/devices/dae/dae_spectra.py @@ -1,9 +1,10 @@ +import numpy as np from ophyd_async.core import SignalR, StandardReadable from ophyd_async.epics.signal import epics_signal_r -import numpy as np + class DaeSpectra(StandardReadable): - def __init__(self, dae_prefix, *, spectra, period, name=""): + def __init__(self, dae_prefix: str, *, spectra: int, period: int, name: str = "") -> None: self.x: SignalR[np.typing.NDArray[np.float32]] = epics_signal_r( np.typing.NDArray[np.float32], f"{dae_prefix}DAE" f":SPEC:{period}:{spectra}:X" ) diff --git a/src/ibex_bluesky_core/devices/dae/dae_tcb_settings.py b/src/ibex_bluesky_core/devices/dae/dae_tcb_settings.py index cecd58d..97f721e 100644 --- a/src/ibex_bluesky_core/devices/dae/dae_tcb_settings.py +++ b/src/ibex_bluesky_core/devices/dae/dae_tcb_settings.py @@ -1,5 +1,5 @@ import xml.etree.ElementTree as ET -from dataclasses import dataclass, field +from dataclasses import dataclass from enum import Enum from typing import Dict from xml.etree.ElementTree import tostring @@ -24,16 +24,22 @@ class TimeUnit(Enum): + """Time unit for DAE TCB settings.""" + MICROSECONDS = 0 NANOSECONDS = 1 class CalculationMethod(Enum): + """Calculation method for DAE TCB settings.""" + SPECIFY_PARAMETERS = 0 USE_TCB_FILE = 1 class TimeRegimeMode(Enum): + """Time Regime Mode options for a single row.""" + BLANK = 0 DT = 1 DTDIVT = 2 @@ -43,6 +49,8 @@ class TimeRegimeMode(Enum): @dataclass class TimeRegimeRow: + """A single time regime row.""" + from_: float | None = None to: float | None = None steps: float | None = None @@ -51,18 +59,22 @@ class TimeRegimeRow: @dataclass class TimeRegime: + """Time regime - contains a dict(rows) which is row_number:TimeRegimeRow.""" + rows: Dict[int, TimeRegimeRow] | None = None @dataclass class DaeTCBSettingsData: + """Dataclass for the DAE TCB settings.""" + tcb_file: str | None = None time_unit: TimeUnit | None = None tcb_tables: Dict[int, TimeRegime] | None = None tcb_calculation_method: CalculationMethod | None = None -def convert_xml_to_tcb_settings(value: str) -> DaeTCBSettingsData: +def _convert_xml_to_tcb_settings(value: str) -> DaeTCBSettingsData: root = ET.fromstring(value) settings_from_xml = convert_xml_to_names_and_values(root) @@ -87,7 +99,7 @@ def convert_xml_to_tcb_settings(value: str) -> DaeTCBSettingsData: ) -def convert_tcb_settings_to_xml(current_xml: str, settings: DaeTCBSettingsData) -> str: +def _convert_tcb_settings_to_xml(current_xml: str, settings: DaeTCBSettingsData) -> str: # get xml here, then substitute values from the dataclasses root = ET.fromstring(current_xml) elements = get_all_elements_in_xml_with_child_called_name(root) @@ -104,20 +116,20 @@ def convert_tcb_settings_to_xml(current_xml: str, settings: DaeTCBSettingsData) class DaeTCBSettings(Device, Locatable): - def __init__(self, dae_prefix, name=""): + def __init__(self, dae_prefix: str, name: str = "") -> None: self.tcb_settings: SignalRW[str] = isis_epics_signal_rw(str, f"{dae_prefix}TCBSETTINGS") super().__init__(name=name) async def locate(self) -> Location: value = await self.tcb_settings.get_value() value_dehexed = dehex_and_decompress(value.encode()).decode() - tcb_settings = convert_xml_to_tcb_settings(value_dehexed) + tcb_settings = _convert_xml_to_tcb_settings(value_dehexed) return {"setpoint": tcb_settings, "readback": tcb_settings} @AsyncStatus.wrap async def set(self, value: DaeTCBSettingsData) -> None: current_xml = await self.tcb_settings.get_value() current_xml_dehexed = dehex_and_decompress(current_xml).decode() - xml = convert_tcb_settings_to_xml(current_xml_dehexed, value) + xml = _convert_tcb_settings_to_xml(current_xml_dehexed, value) the_value_to_write = compress_and_hex(xml).decode() await self.tcb_settings.set(the_value_to_write, wait=True) diff --git a/tests/conftest.py b/tests/conftest.py index 2ecc7b5..3b2fbe1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,6 @@ import pytest from bluesky.run_engine import RunEngine + from ibex_bluesky_core.run_engine import get_run_engine diff --git a/tests/devices/test_block.py b/tests/devices/test_block.py index 9e1733b..323bcc4 100644 --- a/tests/devices/test_block.py +++ b/tests/devices/test_block.py @@ -7,6 +7,8 @@ import bluesky.plan_stubs as bps import bluesky.plans as bp import pytest +from ophyd_async.core import get_mock_put, set_mock_value + from ibex_bluesky_core.devices.block import ( BlockMot, BlockR, @@ -18,7 +20,6 @@ block_rw, block_rw_rbv, ) -from ophyd_async.core import get_mock_put, set_mock_value MOCK_PREFIX = "UNITTEST:MOCK:" diff --git a/tests/devices/test_dae.py b/tests/devices/test_dae.py index ffe62eb..b2b90e7 100644 --- a/tests/devices/test_dae.py +++ b/tests/devices/test_dae.py @@ -3,36 +3,33 @@ from xml.etree import ElementTree as ET import pytest +from ophyd_async.core import get_mock_put from ibex_bluesky_core.devices import compress_and_hex, dehex_and_decompress from ibex_bluesky_core.devices.dae.dae import Dae, RunstateEnum -from ophyd_async.core import get_mock_put - from ibex_bluesky_core.devices.dae.dae_period_settings import ( - DaePeriodSettingsData, DaePeriodSettings, + DaePeriodSettingsData, PeriodSource, PeriodType, SinglePeriodSettings, ) from ibex_bluesky_core.devices.dae.dae_settings import ( DaeSettings, - TimingSource, DaeSettingsData, - convert_xml_to_dae_settings, + TimingSource, ) from ibex_bluesky_core.devices.dae.dae_tcb_settings import ( + CalculationMethod, DaeTCBSettings, DaeTCBSettingsData, - CalculationMethod, TimeRegime, TimeRegimeMode, TimeRegimeRow, TimeUnit, ) -from src.ibex_bluesky_core.devices.dae import set_value_in_dae_xml, convert_xml_to_names_and_values +from src.ibex_bluesky_core.devices.dae import convert_xml_to_names_and_values, set_value_in_dae_xml from src.ibex_bluesky_core.devices.dae.dae_controls import BeginRunExBits -from ophyd_async.core import get_mock_put, set_mock_value MOCK_PREFIX = "UNITTEST:MOCK:" @@ -175,7 +172,7 @@ def test_get_names_and_values_from_xml(): def test_get_names_and_values_without_name_does_not_get_parsed(): - test_xml = f""" + test_xml = """ Cluster @@ -193,7 +190,7 @@ def test_get_names_and_values_without_name_does_not_get_parsed(): def test_get_names_and_values_without_value_does_not_get_parsed(): - test_xml = f""" + test_xml = """ Cluster