Skip to content

Releases: bluesky/ophyd-async

v0.17a2

23 Mar 10:14
eaf5394

Choose a tag to compare

v0.17a2 Pre-release
Pre-release

What's Changed

New Contributors

Full Changelog: v0.17a1...v0.17a2

v0.17a1

05 Mar 17:19
4e49c5f

Choose a tag to compare

v0.17a1 Pre-release
Pre-release

Breaking Change: StandardDetector rewrite

All detector implementations in ophyd-async are updated, external detector implementations need updating:

Detector Controller → Trigger Logic + Arm Logic

# old
class SimController(DetectorController):
    def __init__(self, driver: ADBaseIO):
        self.driver = driver
        
    async def prepare(self, trigger_info: TriggerInfo):
        assert trigger_info.trigger == TriggerInfo.INTERNAL, "Can only do internal"
        await self.driver.num_images.set(trigger_info.number_of_events)
        
    async def arm(self):
        await self.driver.acquire.set(True)
        
    async def wait_for_idle(self):
        await wait_for_value(self.driver.acquire, False, timeout=DEFAULT_TIMEOUT)
        
    async def disarm(self):
        await self.driver.acquire.set(False)

# new  
class SimTriggerLogic(DetectorTriggerLogic):
    def __init__(self, driver: ADBaseIO):
        self.driver = driver
        
    # Also prepare_edge and prepare_level if hardware triggering supported
    async def prepare_internal(self, num: int, livetime: float, deadtime: float):
        await self.driver.num_images.set(num)

# if ADArmLogic is not suitable
class SimArmLogic(DetectorArmLogic):
    def __init__(self, driver: ADBaseIO):
        self.driver = driver
        
    async def arm(self):
        await self.driver.acquire.set(True)
        
    async def wait_for_idle(self):
        await wait_for_value(self.driver.acquire, False, timeout=DEFAULT_TIMEOUT)
        
    async def disarm(self):
        await self.driver.acquire.set(False)

Detector Writer → Data Logic

# old
class ADHDFWriter(DetectorWriter):
    async def open(self, name: str, exposures_per_event: int = 1):
        # Setup file writing
        return describe_dict
        
# new
class ADHDFDataLogic(DetectorDataLogic):
    async def prepare_unbounded(self, detector_name: str):
        # Setup file writing  
        return StreamResourceDataProvider(...)

TriggerInfo Updates

# old
TriggerInfo(
    number_of_events=10,
    trigger=DetectorTrigger.EDGE_TRIGGER,
    livetime=0.1,
    deadtime=0.01,
)

# new  
TriggerInfo(
    number_of_events=10,
    trigger=DetectorTrigger.EXTERNAL_EDGE,
    livetime=0.1,
    deadtime=0.01,
)

Complete SimDetector Example

# old - controller and writer_cls.with_io

from ophyd_async.epics import adcore

class SimDetector(adcore.AreaDetector[SimController]):
    def __init__(
        self,
        prefix: str,
        path_provider: PathProvider,
        drv_suffix="cam1:",
        writer_cls: type[adcore.ADWriter] = adcore.ADHDFWriter,
        fileio_suffix: str | None = None,
        name="",
        config_sigs: Sequence[SignalR] = (),
        plugins: dict[str, adcore.NDPluginBaseIO] | None = None,
    ):
        driver = adcore.ADBaseIO(prefix + drv_suffix)
        controller = SimController(driver)        
        writer = writer_cls.with_io(
            prefix,
            path_provider,
            dataset_source=driver,
            fileio_suffix=fileio_suffix,
            plugins=plugins,
        )        
        super().__init__(
            controller=controller,
            writer=writer,
            plugins=plugins,
            name=name,
            config_sigs=config_sigs,
        )

# new - handled by the baseclass
from ophyd_async.epics import adcore

class SimDetector(adcore.AreaDetector[adcore.ADBaseIO]):
    """Create an ADSimDetector AreaDetector instance."""
    
    def __init__(
        self,
        prefix: str,
        path_provider: PathProvider | None = None,
        driver_suffix="cam1:",
        writer_type: ADWriterType | None = ADWriterType.HDF,
        writer_suffix: str | None = None,
        plugins: dict[str, NDPluginBaseIO] | None = None,
        config_sigs: Sequence[SignalR] = (),
        name: str = "",
    ) -> None:
        driver = adcore.ADBaseIO(prefix + driver_suffix)
        super().__init__(
            prefix=prefix,
            driver=driver,
            arm_logic=adcore.ADArmLogic(driver),
            trigger_logic=SimDetectorTriggerLogic(driver),
            path_provider=path_provider,
            writer_type=writer_type,
            writer_suffix=writer_suffix,
            plugins=plugins,
            config_sigs=config_sigs,
            name=name,
        )

Reading Stats Without Files

# old - not easily supported

# new - use PluginSignalDataLogic
detector = SimDetector(prefix, writer_type=None)
detector.add_detector_logics(PluginSignalDataLogic(driver, stats.total))
# Now stats.total appears in read() without file writing

Multiple Data Streams

# old - required complex inheritance

# new - add multiple data logics
detector = AreaDetector(
    driver=driver,
    arm_logic=ADArmLogic(driver),
    writer_type=None,  # Don't create default writer
)
# Add separate HDF writers for different ROIs
detector.add_detector_logics(
    ADHDFDataLogic(..., datakey_suffix="-roi1"),
    ADHDFDataLogic(..., datakey_suffix="-roi2"),
)

All changes

Full Changelog: v0.16...v0.17

v0.16

17 Feb 16:20
0b4219c

Choose a tag to compare

Breaking changes

Some plan stubs move from ophyd_async.plan_stubs to ophyd_async.fastcs.panda and ophyd_async.epics.adcore

# old
from ophyd_async.plan_stubs import setup_ndattributes, setup_ndstats_sum
# new
from ophyd_async.epics.adcore import setup_ndattributes, setup_ndstats_sum
# old
from ophyd_async.plan_stubs import apply_panda_settings
from ophyd_async.fastcs.panda import apply_panda_settings

What's Changed

Full Changelog: v0.15...v0.16

v0.15

03 Feb 14:57
6ebd5ae

Choose a tag to compare

Notable changes

Remove the wait argument to Signal.set, it is now set when constructing the EPICS Signal.

It can be one of:

  • True: Return when server-side operation has completed
  • False: Return when server-side operation has started
  • callable: Call with the value being put to decide whether to wait

For procedural style devices:

# old
signal = epics_signal_rw(int, "PV")
signal.set(5, wait=False)

# new
signal = epics_signal_rw(int, "PV", wait=False)
signal.set(5)

For declarative style devices:

# old
from ophyd_async.epics.core import PvSuffix

class MyDevice(EpicsDevice):
    signal: A[SignalRW[int], PvSuffix("SUFF")]

my_device.signal.set(5, wait=False)

# new
from ophyd_async.epics.core import PvSuffix, EpicsOptions

class MyDevice(EpicsDevice):
    signal: A[SignalRW[int], PvSuffix("SUFF"), EpicsOptions(wait=False)]

my_device.signal.set(5)

If you need a different value for wait depending on the value you are setting, like for the busy record:

# old
busy_record_signal = epics_signal_rw(bool, "Acquire")
status = busy_record_signal.set(True, wait=True)
busy_record_signal.set(False, wait=False)
await status

# new
from ophyd_async.core import non_zero

busy_record_signal = epics_signal_rw(bool, "Acquire", wait=non_zero)
status = busy_record_signal.set(True)
busy_record_signal.set(False)
await status

Any usage of wait=True (the default) can be removed without any other changes

# old
await signal.set(value, wait=True)  # wait=True is the default

# new
await signal.set(value)

AsyncStatus can be used as a context manager

This avoids issues with dangling tasks at shutdown, and allows more complex logic to be cancelled by a signal set completion.

# old
status = signal1.set(new_value)
async for value in observe_value(signal2, done_status=status):
    if value > threshold:
        break
# warning here if we exit before signal1.set completes

# new
async with signal1.set(new_value):
    async for value in observe_value(signal2):
        if value > threshold:
            break

What's Changed

Full Changelog: v0.14.2...v0.15

v0.14.2

19 Dec 11:19
a221add

Choose a tag to compare

What's Changed

Full Changelog: v0.14.1...v0.14.2

v0.14.1

18 Dec 11:18
9b567b2

Choose a tag to compare

What's Changed

Full Changelog: v0.14.0...v0.14.1

v0.14.0

24 Nov 14:46
3da44d6

Choose a tag to compare

Notable Changes

Helper functions for downstream testing have moved

The following functions:

  • callback_on_mock_put
  • get_mock
  • get_mock_put
  • mock_puts_blocked
  • set_mock_put_proceeds
  • set_mock_value
  • set_mock_values

Have been moved from ophyd_async.testing to ophyd_async.core. They can currently be imported from either but will cause a DeprecationWarning if imported from ophyd_async.testing. The move will be finalised in version 1.0.

What's Changed

New Contributors

Full Changelog: v0.13.7...v0.14.0

v0.13.7

17 Nov 14:50
80ae448

Choose a tag to compare

What's Changed

Full Changelog: v0.13.6...v0.13.7

v0.13.6

04 Nov 09:00
13f67ee

Choose a tag to compare

What's Changed

Full Changelog: v0.13.5...v0.13.6

v0.13.5

27 Oct 12:29
bb7c989

Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v0.13.4...v0.13.5