v0.17
Breaking changes
AreaDetector initialisation
The data writing init parameters for AreaDetector have been consolidated into a single ADWriterFactory argument. Multiple writer factories can be passed. All AreaDetector subclasses in ophyd-async have been updated:
# old
detector = adaravis.AravisDetector("PREFIX:", static_path_provider, writer_cls=adcore.ADWriterType.HDF)
# new
detector = adaravis.AravisDetector("PREFIX:", adcore.ADWriterFactory.hdf(static_path_provider))See https://blueskyproject.io/ophyd-async/main/explanations/decisions/0016-ad-writer-factory.html for rationale
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,
*writer_factories: ADWriterFactory,
driver_suffix="cam1:",
plugins: dict[str, NDPluginBaseIO] | None = None,
config_sigs: Sequence[SignalR] = (),
name: str = "",
) -> None:
driver = ADBaseIO(prefix + driver_suffix)
super().__init__(
driver,
prefix,
*writer_factories,
acquire_logic=ADAcquireLogic(driver),
trigger_logic=SimDetectorTriggerLogic(driver),
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"),
)What's Changed
- 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
- 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
- chore(deps): lock file maintenance by @renovate[bot] in #1232
- chore(deps): lock file maintenance by @renovate[bot] in #1237
- refactor: sum acceleration time and cruise time for PmacTrajectoryTriggerLogic._move_to_start() timeouts by @shihab-dls in #1234
- Lock file maintenance by @renovate[bot] in #1239
- fix: connect all DeviceVector children by @shihab-dls in #1244
- Add on_unstage flag to DetectorArmLogic.disarm by @coretl in #1245
- Lock file maintenance by @renovate[bot] in #1249
- Add note about known bug in derived signals by @DominicOram in #1251
- Lock file maintenance by @renovate[bot] in #1257
- refactor: update EigerDetector and OdinIO to match FastCS changes by @shihab-dls in #1250
- Update dependency https://github.com/DiamondLightSource/python-copier-template to v5.0.3 by @renovate[bot] in #1256
- fix typos by @dihenriksen in #1260
- Add helpers for initializing device vectors from mbbi/mbbo direct record bitmasks. Allow for coercing char/byte/short pv types into 0/1 bools for CA and PVA by @jwlodek in #1238
- Convert PathInfo to ConfinedModel, guarantee that directory_uri is never None" by @jwlodek in #1261
- refactor: Replace DetectorArmLogic with DetectorAcquireLogic by @coretl in #1263
- Loosen typing on ADHDFDataLogic.driver by @DominicOram in #1270
- Write all frames in first h5 file until VDS is working by @jacob720 in #1273
- Only use the RBV PV for the read only signals in the stats plugin by @DominicOram in #1278
- Replace AreaDetector writer parameters with ADWriterFactory by @coretl in #1268
- Add configurable turnaround and ramp up time for PMAC trajectories by @LuisFSegalla in #1262
New Contributors
- @tacaswell made their first contribution in #1208
- @dihenriksen made their first contribution in #1260
- @jacob720 made their first contribution in #1273
Full Changelog: v0.16...v0.17