Releases: bluesky/ophyd-async
v0.17a2
What's Changed
- Remove parent task cancelling behaviour of AsyncStatus context manager by @coretl in #1212
- chore(deps): lock file maintenance by @renovate[bot] in #1215
- Remove CodeQL by @coretl in #1219
- Make set_and_wait_for_value return early if set fails by @coretl in #1217
- Don't cache return datatypes by @rtuck99 in #1222
- makes-pmac-a-pmactrajectorytriggerlogic-class-reference by @gkalua in #1223
- chore(deps): lock file maintenance by @renovate[bot] in #1224
- Move AD signals for backwards compatibility by @DominicOram in #1225
- API: epics AD devices do not reset num_images in trigger by @tacaswell in #1208
- Fixed Tango Signal construction not setting write_trl by @burkeds in #1229
- chore(deps): lock file maintenance by @renovate[bot] in #1230
New Contributors
- @tacaswell made their first contribution in #1208
Full Changelog: v0.17a1...v0.17a2
v0.17a1
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 writingMultiple 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
- Rewrite StandardDetector by @coretl in #1177
- Pass detector name + datakey suffix to providers instead of writer name to match filenames to datakeys. by @jwlodek in #1205
- fix(deps): update dependency pytango to v10.1.3 by @renovate[bot] in #1195
- chore(deps): lock file maintenance by @renovate[bot] in #1206
- Add ADMerlin support by @coretl in #1203
- chore(deps): lock file maintenance by @renovate[bot] in #1207
- Remove pytango pin by @coretl in #1210
- Fix DeviceVector introspection by @shihab-dls in #1198
Full Changelog: v0.16...v0.17
v0.16
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_settingsWhat's Changed
- Improve epics import error by @coretl in #1193
- Update uv lock, pinning tango by @coretl in #1194
- chore(deps): lock file maintenance by @renovate[bot] in #1118
- chore(deps): lock file maintenance by @renovate[bot] in #1196
- chore(deps): lock file maintenance by @renovate[bot] in #1199
- Move plan_stubs that depend on modules other than core into their respective modules by @coretl in #1201
Full Changelog: v0.15...v0.16
v0.15
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 completedFalse: Return when server-side operation has startedcallable: 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 statusAny 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:
breakWhat's Changed
- Improve container socket error message by @hyperrealist in #1174
- Expose AsyncMovable, AsyncLocatable, AsyncPausable publicly by @Tom-Willemsen in #1172
- Support both awaitable keys and values in gather_dict by @coretl in #1175
- Run empty scan when pmac unstage by @shihab-dls in #1169
- chore(deps): update dependency https://github.com/diamondlightsource/python-copier-template to v5.0.2 by @renovate[bot] in #1187
- Add links to interfaces for IO classes by @shihab-dls in #1184
- Remove wait from Signal.set and replace with epics specific argument by @gkalua in #1134
- Optimise mock creation and cache type hinting to speedup tests by @coretl in #1188
- Allow use of
AsyncStatusas a context manager instead of usingobserve_value(done_status=))by @rtuck99 in #1012
Full Changelog: v0.14.2...v0.15
v0.14.2
What's Changed
- 1163 support signal[t] | none syntax in devicefiller by @LuisFSegalla in #1165
- Make test_observe more verbose by @coretl in #1167
- Improve error when raw2derived argument has no type by @Villtord in #1135
Full Changelog: v0.14.1...v0.14.2
v0.14.1
What's Changed
- Fixing precision not being able to handle non-floats by @burkeds in #1141
- Improve tango test stability by @hyperrealist in #1146
- Update dependency https://github.com/DiamondLightSource/python-copier-template to v5.0.0 by @renovate[bot] in #1155
- Update dependency https://github.com/DiamondLightSource/python-copier-template to v5.0.1 by @renovate[bot] in #1156
- Add auto max incrementing path provider by @olliesilvester in #1147
- Make nexpi PV optional so that it can be used on eiger's without extg by @RJCD-Diamond in #1152
- makes_wait_for_idle_await_arm_status by @gkalua in #1157
- Add claude PR review by @coretl in #1160
- Change the trigger for claude by @coretl in #1161
- Tweak claude by @coretl in #1162
- Calculate longest motor move time and set timeout by @shihab-dls in #1164
Full Changelog: v0.14.0...v0.14.1
v0.14.0
Notable Changes
Helper functions for downstream testing have moved
The following functions:
callback_on_mock_putget_mockget_mock_putmock_puts_blockedset_mock_put_proceedsset_mock_valueset_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
- Implement DeviceMock with automatic logic injection for mock devices by @vredchenko in #1129
- Make vscde discover all tests by @hyperrealist in #1142
- Update dependency https://github.com/DiamondLightSource/python-copier-template to v5.0.0a5 by @renovate[bot] in #1143
New Contributors
- @vredchenko made their first contribution in #1129
Full Changelog: v0.13.7...v0.14.0
v0.13.7
What's Changed
- PMAC: Get cs info from cs motors by @gkalua in #1120
- Use dial limit for motor limit check by @teoching0705 in #1124
- Removing unnecessary TangoReadable class by @burkeds in #1130
- Made
set_and_wait_for_other_valuestill still raise a TimeoutError if there is no__name__on the matcher by @DominicOram in #1123 - Unify fixture invocation by @hyperrealist in #1080
Full Changelog: v0.13.6...v0.13.7
v0.13.6
What's Changed
- Allow mock signals to have different readback to setpoint values by @DominicOram in #1096
- Lock file maintenance by @renovate[bot] in #1109
- Quantize calculated turnaround points in pmac by @shihab-dls in #1105
- Add enable signal to panda PulseBlock by @shihab-dls in #1115
- Update dependency https://github.com/DiamondLightSource/python-copier-template to v5.0.0a4 by @renovate[bot] in #1112
Full Changelog: v0.13.5...v0.13.6
v0.13.5
What's Changed
- Restructure tests by @hyperrealist in #1055
- improves error message from datakey shape function by @gkalua in #1073
- Copier update to uv by @coretl in #1058
- Configure Renovate by @renovate[bot] in #1076
- Update hoverkraft-tech/compose-action action to v2.4.0 by @renovate[bot] in #1078
- Disable pyenv notifications for python version by @coretl in #1085
- Making tango event subscription asynchronous by @burkeds in #1001
- update to use new Subscribable protocol by @jsouter in #950
- Tango improved descriptor by @burkeds in #865
- Lock file maintenance by @renovate[bot] in #1082
- Add note on using Annotated with ruff by @DominicOram in #1090
- Improve renovate config by @coretl in #1088
- Enable automerge for lockFileMaintenance by @coretl in #1094
- Add Yes No strict enum by @olliesilvester in #1067
- Modify tango dependency version constraint by @coretl in #1093
- Update github/codeql-action action to v4 by @renovate[bot] in #1092
- Update hoverkraft-tech/compose-action action to v2.4.1 by @renovate[bot] in #1091
- Use the same name for all caught exceptions by @IgpayAtenlay in #1098
- Lock file maintenance by @renovate[bot] in #1097
- Update assert_emitted to use Mapping by @oliwenmandiamond in #1101
New Contributors
- @renovate[bot] made their first contribution in #1076
- @IgpayAtenlay made their first contribution in #1098
Full Changelog: v0.13.4...v0.13.5