ophyd-async 0.3a2__tar.gz → 0.3a4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/workflows/_docs.yml +1 -1
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/PKG-INFO +20 -3
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/README.md +16 -1
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/conf.py +5 -9
- ophyd_async-0.3a4/docs/examples/foo_detector.py +82 -0
- ophyd_async-0.3a4/docs/explanations/design-goals.rst +57 -0
- ophyd_async-0.3a4/docs/explanations/flyscanning.rst +63 -0
- ophyd_async-0.3a4/docs/how-to/choose-interfaces-for-devices.md +15 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/how-to/make-a-simple-device.rst +2 -1
- ophyd_async-0.3a4/docs/how-to/make-a-standard-detector.rst +64 -0
- ophyd_async-0.3a4/docs/how-to/write-tests-for-devices.rst +58 -0
- ophyd_async-0.3a4/docs/images/hardware-triggered-scan.png +0 -0
- ophyd_async-0.3a4/docs/images/outer-scan.png +0 -0
- ophyd_async-0.3a4/docs/images/simple-hardware-scan.png +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/tutorials/using-existing-devices.rst +1 -1
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/pyproject.toml +3 -2
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/_version.py +1 -1
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/core/__init__.py +35 -11
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/core/async_status.py +2 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/core/detector.py +8 -9
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/core/device.py +22 -9
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/core/flyer.py +2 -2
- ophyd_async-0.3a4/src/ophyd_async/core/mock_signal_backend.py +86 -0
- ophyd_async-0.3a4/src/ophyd_async/core/mock_signal_utils.py +149 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/core/signal.py +140 -49
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/core/signal_backend.py +2 -2
- ophyd_async-0.3a2/src/ophyd_async/core/sim_signal_backend.py → ophyd_async-0.3a4/src/ophyd_async/core/soft_signal_backend.py +29 -39
- ophyd_async-0.3a4/src/ophyd_async/core/standard_readable.py +261 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/_backend/_aioca.py +17 -13
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/_backend/_p4p.py +28 -18
- ophyd_async-0.3a4/src/ophyd_async/epics/_backend/common.py +25 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/__init__.py +4 -4
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/aravis.py +7 -9
- ophyd_async-0.3a4/src/ophyd_async/epics/areadetector/controllers/__init__.py +5 -0
- ophyd_async-0.3a4/src/ophyd_async/epics/areadetector/controllers/kinetix_controller.py +49 -0
- ophyd_async-0.3a4/src/ophyd_async/epics/areadetector/controllers/vimba_controller.py +66 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/drivers/__init__.py +6 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/drivers/ad_base.py +12 -10
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/drivers/aravis_driver.py +7 -5
- ophyd_async-0.3a4/src/ophyd_async/epics/areadetector/drivers/kinetix_driver.py +27 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/drivers/pilatus_driver.py +5 -2
- ophyd_async-0.3a4/src/ophyd_async/epics/areadetector/drivers/vimba_driver.py +63 -0
- ophyd_async-0.3a4/src/ophyd_async/epics/areadetector/kinetix.py +46 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/pilatus.py +7 -12
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/single_trigger_det.py +14 -6
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/utils.py +2 -12
- ophyd_async-0.3a4/src/ophyd_async/epics/areadetector/vimba.py +43 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/writers/hdf_writer.py +6 -3
- ophyd_async-0.3a4/src/ophyd_async/epics/areadetector/writers/nd_file_hdf.py +42 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/writers/nd_plugin.py +6 -7
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/demo/__init__.py +19 -22
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/motion/motor.py +16 -13
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/pvi/pvi.py +11 -11
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/signal/signal.py +1 -1
- ophyd_async-0.3a4/src/ophyd_async/log.py +130 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/panda/_hdf_panda.py +3 -3
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/panda/writers/_hdf_writer.py +3 -3
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/protocols.py +26 -3
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/sim/demo/sim_motor.py +14 -12
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/sim/pattern_generator.py +9 -9
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/sim/sim_pattern_detector_writer.py +2 -2
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/sim/sim_pattern_generator.py +2 -2
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async.egg-info/PKG-INFO +20 -3
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async.egg-info/SOURCES.txt +24 -2
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async.egg-info/requires.txt +3 -1
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/conftest.py +17 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/core/test_device.py +10 -3
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/core/test_device_collector.py +18 -18
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/core/test_device_save_loader.py +1 -1
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/core/test_flyer.py +7 -8
- ophyd_async-0.3a4/tests/core/test_mock_signal_backend.py +341 -0
- ophyd_async-0.3a4/tests/core/test_signal.py +243 -0
- ophyd_async-0.3a2/tests/core/test_sim.py → ophyd_async-0.3a4/tests/core/test_soft_signal_backend.py +13 -18
- ophyd_async-0.3a4/tests/core/test_standard_readable.py +221 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/core/test_utils.py +24 -14
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/_backend/test_common.py +12 -18
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/areadetector/test_aravis.py +21 -46
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/areadetector/test_controllers.py +2 -2
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/areadetector/test_drivers.py +5 -5
- ophyd_async-0.3a4/tests/epics/areadetector/test_kinetix.py +125 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/areadetector/test_pilatus.py +9 -48
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/areadetector/test_scans.py +5 -5
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/areadetector/test_single_trigger_det.py +7 -7
- ophyd_async-0.3a4/tests/epics/areadetector/test_vimba.py +137 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/areadetector/test_writers.py +4 -4
- ophyd_async-0.3a4/tests/epics/demo/test_demo.py +330 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/demo/test_demo_ad_sim_detector.py +22 -22
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/motion/test_motor.py +21 -19
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/test_pvi.py +13 -9
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/test_signals.py +39 -7
- ophyd_async-0.3a4/tests/panda/test_hdf_panda.py +172 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/panda/test_panda_connect.py +18 -16
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/panda/test_panda_controller.py +12 -11
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/panda/test_panda_utils.py +13 -11
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/panda/test_trigger.py +10 -8
- ophyd_async-0.3a4/tests/panda/test_writer.py +208 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/protocols/test_protocols.py +2 -9
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/sim/conftest.py +1 -1
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/sim/demo/test_sim_motor.py +29 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/sim/test_sim_detector.py +2 -2
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/sim/test_sim_writer.py +1 -1
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/sim/test_streaming_plan.py +1 -7
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/test_flyer_with_panda.py +20 -18
- ophyd_async-0.3a4/tests/test_log.py +88 -0
- ophyd_async-0.3a2/docs/how-to/write-tests-for-devices.rst +0 -45
- ophyd_async-0.3a2/src/ophyd_async/core/standard_readable.py +0 -74
- ophyd_async-0.3a2/src/ophyd_async/epics/_backend/common.py +0 -25
- ophyd_async-0.3a2/src/ophyd_async/epics/areadetector/controllers/__init__.py +0 -4
- ophyd_async-0.3a2/src/ophyd_async/epics/areadetector/writers/nd_file_hdf.py +0 -39
- ophyd_async-0.3a2/tests/core/test_signal.py +0 -155
- ophyd_async-0.3a2/tests/epics/demo/test_demo.py +0 -296
- ophyd_async-0.3a2/tests/panda/test_hdf_panda.py +0 -210
- ophyd_async-0.3a2/tests/panda/test_writer.py +0 -202
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.codecov.yml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.copier-answers.yml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.devcontainer/devcontainer.json +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.git-blame-ignore-revs +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/CONTRIBUTING.md +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/actions/install_requirements/action.yml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/dependabot.yml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/pages/index.html +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/pages/make_switcher.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/workflows/_check.yml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/workflows/_dist.yml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/workflows/_pypi.yml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/workflows/_release.yml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/workflows/_test.yml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/workflows/_tox.yml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/workflows/ci.yml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.github/workflows/periodic.yml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.gitignore +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.mailmap +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/.pre-commit-config.yaml +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/Dockerfile +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/LICENSE +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/_templates/README +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/_templates/custom-class-template.rst +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/_templates/custom-module-template.rst +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/examples/epics_demo.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/explanations/decisions/0001-record-architecture-decisions.md +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/explanations/decisions/0002-switched-to-python-copier-template.md +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/explanations/decisions/0003-ophyd-async-migration.rst +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/explanations/decisions/0004-repository-structure.rst +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/explanations/decisions/0005-respect-black-line-length.rst +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/explanations/decisions/0006-procedural-device-definitions.rst +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/explanations/decisions/COPYME +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/explanations/decisions.md +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/explanations/event-loop-choice.rst +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/explanations.md +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/genindex.rst +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/how-to/compound-devices.rst +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/how-to/contribute.md +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/how-to.md +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/images/bluesky_ophyd_epics_devices_logo.svg +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/images/bluesky_ophyd_logo.svg +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/images/ophyd_favicon.svg +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/index.md +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/reference/api.rst +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/reference.md +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/tutorials/installation.md +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/docs/tutorials.md +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/setup.cfg +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/__main__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/core/_providers.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/core/device_save_loader.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/core/utils.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/_backend/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/controllers/ad_sim_controller.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/controllers/aravis_controller.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/controllers/pilatus_controller.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/writers/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/writers/_hdfdataset.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/areadetector/writers/_hdffile.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/demo/demo_ad_sim_detector.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/demo/mover.db +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/demo/sensor.db +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/motion/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/pvi/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/signal/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/epics/signal/_epics_transport.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/panda/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/panda/_common_blocks.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/panda/_panda_controller.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/panda/_table.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/panda/_trigger.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/panda/_utils.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/panda/writers/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/panda/writers/_panda_hdf_file.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/planstubs/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/planstubs/prepare_trigger_and_dets.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/sim/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/sim/demo/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async/sim/sim_pattern_detector_control.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async.egg-info/dependency_links.txt +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async.egg-info/entry_points.txt +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/src/ophyd_async.egg-info/top_level.txt +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/core/test_async_status.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/areadetector/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/areadetector/test_utils.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/motion/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/epics/test_records.db +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/panda/db/panda.db +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/panda/test_table.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/sim/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/sim/demo/__init__.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/sim/test_pattern_generator.py +0 -0
- {ophyd_async-0.3a2 → ophyd_async-0.3a4}/tests/test_cli.py +0 -0
|
@@ -47,7 +47,7 @@ jobs:
|
|
|
47
47
|
if: github.ref_type == 'tag' || github.ref_name == 'main'
|
|
48
48
|
# We pin to the SHA, not the tag, for security reasons.
|
|
49
49
|
# https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions
|
|
50
|
-
uses: peaceiris/actions-gh-pages@
|
|
50
|
+
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
|
|
51
51
|
with:
|
|
52
52
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
53
53
|
publish_dir: .github/pages
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ophyd-async
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3a4
|
|
4
4
|
Summary: Asynchronous Bluesky hardware abstraction code, compatible with control systems like EPICS and Tango
|
|
5
5
|
Author-email: Tom Cobb <tom.cobb@diamond.ac.uk>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -48,6 +48,7 @@ Requires-Dist: bluesky>=1.13.0a3
|
|
|
48
48
|
Requires-Dist: event-model<1.21.0
|
|
49
49
|
Requires-Dist: p4p
|
|
50
50
|
Requires-Dist: pyyaml
|
|
51
|
+
Requires-Dist: colorlog
|
|
51
52
|
Provides-Extra: ca
|
|
52
53
|
Requires-Dist: aioca>=1.6; extra == "ca"
|
|
53
54
|
Provides-Extra: pva
|
|
@@ -72,7 +73,7 @@ Requires-Dist: pipdeptree; extra == "dev"
|
|
|
72
73
|
Requires-Dist: pre-commit; extra == "dev"
|
|
73
74
|
Requires-Dist: pydata-sphinx-theme>=0.12; extra == "dev"
|
|
74
75
|
Requires-Dist: pyepics>=3.4.2; extra == "dev"
|
|
75
|
-
Requires-Dist: pyside6==6.
|
|
76
|
+
Requires-Dist: pyside6==6.7.0; extra == "dev"
|
|
76
77
|
Requires-Dist: pytest; extra == "dev"
|
|
77
78
|
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
78
79
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -81,6 +82,7 @@ Requires-Dist: pytest-rerunfailures; extra == "dev"
|
|
|
81
82
|
Requires-Dist: pytest-timeout; extra == "dev"
|
|
82
83
|
Requires-Dist: ruff; extra == "dev"
|
|
83
84
|
Requires-Dist: sphinx-autobuild; extra == "dev"
|
|
85
|
+
Requires-Dist: sphinxcontrib-mermaid; extra == "dev"
|
|
84
86
|
Requires-Dist: sphinx-copybutton; extra == "dev"
|
|
85
87
|
Requires-Dist: sphinx-design; extra == "dev"
|
|
86
88
|
Requires-Dist: tox-direct; extra == "dev"
|
|
@@ -92,7 +94,7 @@ Requires-Dist: types-pyyaml; extra == "dev"
|
|
|
92
94
|
[](https://pypi.org/project/ophyd-async)
|
|
93
95
|
[](https://opensource.org/licenses/BSD-3-Clause)
|
|
94
96
|
|
|
95
|
-
#
|
|
97
|
+
# ophyd-async
|
|
96
98
|
|
|
97
99
|
Asynchronous Bluesky hardware abstraction code, compatible with control systems like EPICS and Tango
|
|
98
100
|
|
|
@@ -102,6 +104,21 @@ Asynchronous Bluesky hardware abstraction code, compatible with control systems
|
|
|
102
104
|
| Documentation | <https://bluesky.github.io/ophyd-async> |
|
|
103
105
|
| Releases | <https://github.com/bluesky/ophyd-async/releases> |
|
|
104
106
|
|
|
107
|
+
Ophyd-async is a Python library for asynchronously interfacing with hardware, intended to
|
|
108
|
+
be used as an abstraction layer that enables experiment orchestration and data acquisition code to operate above the specifics of particular devices and control
|
|
109
|
+
systems.
|
|
110
|
+
|
|
111
|
+
Both ophyd and ophyd-async are typically used with the [Bluesky Run Engine][] for experiment orchestration and data acquisition.
|
|
112
|
+
|
|
113
|
+
While [EPICS][] is the most common control system layer that ophyd-async can interface with, support for other control systems like [Tango][] will be supported in the future. The focus of ophyd-async is:
|
|
114
|
+
|
|
115
|
+
* Asynchronous signal access, opening the possibility for hardware-triggered scanning (also known as fly-scanning)
|
|
116
|
+
* Simpler instantiation of devices (groupings of signals) with less reliance upon complex class hierarchies
|
|
117
|
+
|
|
118
|
+
[Bluesky Run Engine]: http://blueskyproject.io/bluesky
|
|
119
|
+
[EPICS]: http://www.aps.anl.gov/epics/
|
|
120
|
+
[Tango]: https://www.tango-controls.org/
|
|
121
|
+
|
|
105
122
|
<!-- README only content. Anything below this line won't be included in index.md -->
|
|
106
123
|
|
|
107
124
|
See https://bluesky.github.io/ophyd-async for more detailed documentation.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://pypi.org/project/ophyd-async)
|
|
4
4
|
[](https://opensource.org/licenses/BSD-3-Clause)
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# ophyd-async
|
|
7
7
|
|
|
8
8
|
Asynchronous Bluesky hardware abstraction code, compatible with control systems like EPICS and Tango
|
|
9
9
|
|
|
@@ -13,6 +13,21 @@ Asynchronous Bluesky hardware abstraction code, compatible with control systems
|
|
|
13
13
|
| Documentation | <https://bluesky.github.io/ophyd-async> |
|
|
14
14
|
| Releases | <https://github.com/bluesky/ophyd-async/releases> |
|
|
15
15
|
|
|
16
|
+
Ophyd-async is a Python library for asynchronously interfacing with hardware, intended to
|
|
17
|
+
be used as an abstraction layer that enables experiment orchestration and data acquisition code to operate above the specifics of particular devices and control
|
|
18
|
+
systems.
|
|
19
|
+
|
|
20
|
+
Both ophyd and ophyd-async are typically used with the [Bluesky Run Engine][] for experiment orchestration and data acquisition.
|
|
21
|
+
|
|
22
|
+
While [EPICS][] is the most common control system layer that ophyd-async can interface with, support for other control systems like [Tango][] will be supported in the future. The focus of ophyd-async is:
|
|
23
|
+
|
|
24
|
+
* Asynchronous signal access, opening the possibility for hardware-triggered scanning (also known as fly-scanning)
|
|
25
|
+
* Simpler instantiation of devices (groupings of signals) with less reliance upon complex class hierarchies
|
|
26
|
+
|
|
27
|
+
[Bluesky Run Engine]: http://blueskyproject.io/bluesky
|
|
28
|
+
[EPICS]: http://www.aps.anl.gov/epics/
|
|
29
|
+
[Tango]: https://www.tango-controls.org/
|
|
30
|
+
|
|
16
31
|
<!-- README only content. Anything below this line won't be included in index.md -->
|
|
17
32
|
|
|
18
33
|
See https://bluesky.github.io/ophyd-async for more detailed documentation.
|
|
@@ -32,6 +32,8 @@ else:
|
|
|
32
32
|
version = release
|
|
33
33
|
|
|
34
34
|
extensions = [
|
|
35
|
+
# for diagrams
|
|
36
|
+
"sphinxcontrib.mermaid",
|
|
35
37
|
# Use this for generating API docs
|
|
36
38
|
"sphinx.ext.autodoc",
|
|
37
39
|
"sphinx.ext.doctest",
|
|
@@ -57,6 +59,9 @@ extensions = [
|
|
|
57
59
|
"numpydoc",
|
|
58
60
|
]
|
|
59
61
|
|
|
62
|
+
# So we can use the ::: syntax
|
|
63
|
+
myst_enable_extensions = ["colon_fence"]
|
|
64
|
+
|
|
60
65
|
napoleon_google_docstring = False
|
|
61
66
|
napoleon_numpy_docstring = True
|
|
62
67
|
|
|
@@ -184,11 +189,6 @@ html_theme_options = {
|
|
|
184
189
|
"url": f"https://pypi.org/project/{project}",
|
|
185
190
|
"icon": "fas fa-cube",
|
|
186
191
|
},
|
|
187
|
-
{
|
|
188
|
-
"name": "Gitter",
|
|
189
|
-
"url": "https://gitter.im/NSLS-II/DAMA",
|
|
190
|
-
"icon": "fas fa-person-circle-question",
|
|
191
|
-
},
|
|
192
192
|
],
|
|
193
193
|
"switcher": {
|
|
194
194
|
"json_url": switcher_json,
|
|
@@ -201,10 +201,6 @@ html_theme_options = {
|
|
|
201
201
|
"name": "Bluesky Project",
|
|
202
202
|
"url": "https://blueskyproject.io",
|
|
203
203
|
},
|
|
204
|
-
{
|
|
205
|
-
"name": "Release Notes",
|
|
206
|
-
"url": f"https://github.com/{github_user}/{github_repo}/releases",
|
|
207
|
-
},
|
|
208
204
|
],
|
|
209
205
|
"navigation_with_keys": False,
|
|
210
206
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from bluesky.protocols import HasHints, Hints
|
|
5
|
+
|
|
6
|
+
from ophyd_async.core import DirectoryProvider
|
|
7
|
+
from ophyd_async.core.async_status import AsyncStatus
|
|
8
|
+
from ophyd_async.core.detector import DetectorControl, DetectorTrigger, StandardDetector
|
|
9
|
+
from ophyd_async.epics.areadetector.drivers.ad_base import (
|
|
10
|
+
ADBase,
|
|
11
|
+
ADBaseShapeProvider,
|
|
12
|
+
start_acquiring_driver_and_ensure_status,
|
|
13
|
+
)
|
|
14
|
+
from ophyd_async.epics.areadetector.utils import ImageMode, ad_rw, stop_busy_record
|
|
15
|
+
from ophyd_async.epics.areadetector.writers.hdf_writer import HDFWriter
|
|
16
|
+
from ophyd_async.epics.areadetector.writers.nd_file_hdf import NDFileHDF
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FooDriver(ADBase):
|
|
20
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
21
|
+
self.trigger_mode = ad_rw(str, prefix + "TriggerMode")
|
|
22
|
+
super().__init__(prefix, name)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class FooController(DetectorControl):
|
|
26
|
+
def __init__(self, driver: FooDriver) -> None:
|
|
27
|
+
self._drv = driver
|
|
28
|
+
|
|
29
|
+
def get_deadtime(self, exposure: float) -> float:
|
|
30
|
+
# FooDetector deadtime handling
|
|
31
|
+
return 0.001
|
|
32
|
+
|
|
33
|
+
async def arm(
|
|
34
|
+
self,
|
|
35
|
+
num: int,
|
|
36
|
+
trigger: DetectorTrigger = DetectorTrigger.internal,
|
|
37
|
+
exposure: Optional[float] = None,
|
|
38
|
+
) -> AsyncStatus:
|
|
39
|
+
await asyncio.gather(
|
|
40
|
+
self._drv.num_images.set(num),
|
|
41
|
+
self._drv.image_mode.set(ImageMode.multiple),
|
|
42
|
+
self._drv.trigger_mode.set(f"FOO{trigger}"),
|
|
43
|
+
)
|
|
44
|
+
if exposure is not None:
|
|
45
|
+
await self._drv.acquire_time.set(exposure)
|
|
46
|
+
return await start_acquiring_driver_and_ensure_status(self._drv)
|
|
47
|
+
|
|
48
|
+
async def disarm(self):
|
|
49
|
+
await stop_busy_record(self._drv.acquire, False, timeout=1)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class FooDetector(StandardDetector, HasHints):
|
|
53
|
+
_controller: FooController
|
|
54
|
+
_writer: HDFWriter
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
prefix: str,
|
|
59
|
+
directory_provider: DirectoryProvider,
|
|
60
|
+
drv_suffix="cam1:",
|
|
61
|
+
hdf_suffix="HDF1:",
|
|
62
|
+
name="",
|
|
63
|
+
):
|
|
64
|
+
# Must be children to pick up connect
|
|
65
|
+
self.drv = FooDriver(prefix + drv_suffix)
|
|
66
|
+
self.hdf = NDFileHDF(prefix + hdf_suffix)
|
|
67
|
+
|
|
68
|
+
super().__init__(
|
|
69
|
+
FooController(self.drv),
|
|
70
|
+
HDFWriter(
|
|
71
|
+
self.hdf,
|
|
72
|
+
directory_provider,
|
|
73
|
+
lambda: self.name,
|
|
74
|
+
ADBaseShapeProvider(self.drv),
|
|
75
|
+
),
|
|
76
|
+
config_sigs=(self.drv.acquire_time,),
|
|
77
|
+
name=name,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def hints(self) -> Hints:
|
|
82
|
+
return self._writer.hints
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Design Goals
|
|
2
|
+
============
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
Parity with Ophyd
|
|
6
|
+
-----------------
|
|
7
|
+
|
|
8
|
+
It should be possible to migrate applications that use ophyd_ to ophyd-async. Meaning it must support:
|
|
9
|
+
|
|
10
|
+
- Definition of devices
|
|
11
|
+
- Conformity to the bluesky protocols
|
|
12
|
+
- Epics (ChannelAccess) as a backend
|
|
13
|
+
|
|
14
|
+
Ophyd-async should provide built-in support logic for controlling `the same set of devices as ophyd <https://blueskyproject.io/ophyd/user/reference/builtin-devices.html>`_.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
Clean Device Definition
|
|
18
|
+
-----------------------
|
|
19
|
+
|
|
20
|
+
It should be easy to define devices with signals that talk to multiple backends and to cleanly organize device logic via composition.
|
|
21
|
+
|
|
22
|
+
We need to be able to:
|
|
23
|
+
|
|
24
|
+
- Separate the Device interface from the multiple pieces of logic that might use that Device in a particular way
|
|
25
|
+
- Define that Signals of a particular type exist without creating them so backends like Tango or EPICS + PVI can fill them in
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
Parity with Malcolm
|
|
29
|
+
-------------------
|
|
30
|
+
|
|
31
|
+
.. seealso:: `./flyscanning`
|
|
32
|
+
|
|
33
|
+
Ophyd-async should provide the same building blocks for defining flyscans scans as malcolm_. It should support PandA and Zebra as timing masters by default, but also provide easy helpers for developers to write support for their own devices.
|
|
34
|
+
|
|
35
|
+
It should enable `motor trajectory scanning <motortraj_>` and `multiple triggering rates<detectorsync_>` based around a base rate, and pausing/resuming scans. Scans should be modelled using scanspec_, which serves as a universal language for defining trajectory and time-resolved scans, and converted to the underlying format of the given motion controller. It should also be possible to define an `outer scan <outerscan_>`.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
Improved Trajectory Calculation
|
|
39
|
+
-------------------------------
|
|
40
|
+
|
|
41
|
+
Ophyd-async will provide and improve upon the algorithms that malcolm_ uses to calculate trajectories for supported hardware.
|
|
42
|
+
|
|
43
|
+
The EPICS pmac_ module supports trajectory scanning, specifying a growing array of positions, velocities and time for axes to move through to perform a scan.
|
|
44
|
+
Ophyd-async will provide mechanisms for specifying these scans via a scanspec_, calculating run-ups and turnarounds based on motor parameters, keeping the trajectory scan arrays filled based on the ScanSpec, and allowing this scan to be paused and resumed.
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
Outstanding Design Decisions
|
|
48
|
+
----------------------------
|
|
49
|
+
|
|
50
|
+
To view and contribute to discussions on outstanding decisions, please see the design_ label in our Github issues.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
.. _ophyd: https://github.com/bluesky/ophyd
|
|
54
|
+
.. _malcolm: https://github.com/dls-controls/pymalcolm
|
|
55
|
+
.. _scanspec: https://github.com/dls-controls/scanspec
|
|
56
|
+
.. _design: https://github.com/bluesky/ophyd-async/issues?q=is%3Aissue+is%3Aopen+label%3Adesign
|
|
57
|
+
.. _pmac: https://github.com/dls-controls/pmac
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Flyscanning
|
|
2
|
+
===========
|
|
3
|
+
|
|
4
|
+
Flyscanning (also known as hardware triggered scanning, asynchronous acquisition, and hardware synchronized scanning) is the practice of accelerating the rate of data collection by handing control over to an external hardware system that can control and synchronize the triggering of detectors with other signals and/or commands. Flyscans take many forms.
|
|
5
|
+
|
|
6
|
+
.. _detectorsync_:
|
|
7
|
+
|
|
8
|
+
Detector Synchronization
|
|
9
|
+
------------------------
|
|
10
|
+
|
|
11
|
+
.. figure:: ../images/simple-hardware-scan.png
|
|
12
|
+
:alt: hardware-triggered setup
|
|
13
|
+
:width: 300
|
|
14
|
+
|
|
15
|
+
A triggering system can send pulses to two or more detectors to make them expose simultaneously, or at different multiples of the same base rate (e.g. 200Hz and 400Hz).
|
|
16
|
+
|
|
17
|
+
.. _motortraj_:
|
|
18
|
+
|
|
19
|
+
Motor Trajectory Scanning
|
|
20
|
+
-------------------------
|
|
21
|
+
|
|
22
|
+
.. figure:: ../images/hardware-triggered-scan.png
|
|
23
|
+
:alt: trajectory scanning setup
|
|
24
|
+
|
|
25
|
+
The triggering system can be configured to trigger the detectors at the same time as the motion controller commands the motors to go to certain points, or even exactly when they reach those points, using the readback values. This can be achieved on the scale of microseconds/nanoseconds, in comparison to traditional soft scans controlled via a network, which normally synchronize on the scale of seconds.
|
|
26
|
+
|
|
27
|
+
.. _outerscan_:
|
|
28
|
+
|
|
29
|
+
Outer Scanning
|
|
30
|
+
--------------
|
|
31
|
+
|
|
32
|
+
Outer scans are flyscans nested inside soft scans.
|
|
33
|
+
|
|
34
|
+
.. figure:: ../images/outer-scan.png
|
|
35
|
+
:alt: hardware-triggered setup
|
|
36
|
+
|
|
37
|
+
In the example above a 2D grid scan in ``x`` and ``y`` is repeated in a third dimension: ``z``. Given that ``z`` only needs to move for every 1 in every 25 points, it could be synchronized via software rather than hardware without significantly affecting scan time (and saving the effort/expense of wiring it into a triggering system). It then becomes the responsibility of the software to move ``z``, hand control to the external hardware, wait for one grid's worth of points, take control back, and repeat.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
Hardware
|
|
41
|
+
--------
|
|
42
|
+
|
|
43
|
+
Ophyd-async ships with support for Quantum Detectors' PandA_ and Zebra_ as triggering mechanisms.
|
|
44
|
+
|
|
45
|
+
These are very modular and can be used to trigger a variety of detectors and handle readback signals from a variety of sample control devices. See full specs for more information.
|
|
46
|
+
|
|
47
|
+
It is possible to write support for additional systems/devices.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
Role of Ophyd-Async
|
|
51
|
+
-------------------
|
|
52
|
+
|
|
53
|
+
Bluesky supports devices that configure acquisition and then hand over control to an external system via the ``Flyer`` protocol.
|
|
54
|
+
|
|
55
|
+
Ophyd-async's job is to provide devices that implement ``Flyer`` and can:
|
|
56
|
+
|
|
57
|
+
- Configure all necessary hardware for a scan
|
|
58
|
+
- Kickoff a scan and monitor progress until complete
|
|
59
|
+
- Produce documents representing the progress of the scan
|
|
60
|
+
- Allow handing control back and forth to enable outer scanning
|
|
61
|
+
|
|
62
|
+
.. _PandA: https://quantumdetectors.com/products/pandabox/
|
|
63
|
+
.. _Zebra: https://quantumdetectors.com/products/zebra/
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Decision Flowchart for Creating a New ophyd_async Device
|
|
2
|
+
|
|
3
|
+
This document contains a decision flowchart designed to guide developers through the process of creating a new ophyd_async device in the Ophyd library. It outlines a series of decisions based on the device's capabilities, such as file writing, reading values from process variables (PVs), and mobility within scans. The flowchart helps in determining the appropriate class inheritance and methods to override for optimal device functionality and integration into the system.
|
|
4
|
+
|
|
5
|
+
```{mermaid}
|
|
6
|
+
|
|
7
|
+
flowchart TD
|
|
8
|
+
start([Start]) --> isFileWriting{Is it a File Writing Detector?}
|
|
9
|
+
isFileWriting -- Yes --> useStandardDetector[Use StandardDetector]
|
|
10
|
+
isFileWriting -- No --> producesPVValue{Does it produce a value from a PV you want to read in a scan?}
|
|
11
|
+
producesPVValue -- Yes --> isMovable{Is it something that you move in a scan?}
|
|
12
|
+
isMovable -- Yes --> useReadableMovable[Use StandardReadable + AsyncMovable + Override set method]
|
|
13
|
+
isMovable -- No --> useReadable[Use StandardReadable]
|
|
14
|
+
producesPVValue -- No --> useDevice[Use Device]
|
|
15
|
+
```
|
|
@@ -30,7 +30,8 @@ its Python type, which could be:
|
|
|
30
30
|
|
|
31
31
|
- A primitive (`str`, `int`, `float`)
|
|
32
32
|
- An array (`numpy.typing.NDArray` or ``Sequence[str]``)
|
|
33
|
-
- An enum (`enum.Enum`)
|
|
33
|
+
- An enum (`enum.Enum`) which **must** also extend `str`
|
|
34
|
+
- `str` and ``EnumClass(str, Enum)`` are the only valid ``datatype`` for an enumerated signal.
|
|
34
35
|
|
|
35
36
|
The rest of the arguments are PV connection information, in this case the PV suffix.
|
|
36
37
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
.. note::
|
|
2
|
+
|
|
3
|
+
Ophyd async is included on a provisional basis until the v1.0 release and
|
|
4
|
+
may change API on minor release numbers before then
|
|
5
|
+
|
|
6
|
+
Make a StandardDetector
|
|
7
|
+
=======================
|
|
8
|
+
|
|
9
|
+
`StandardDetector` is an abstract class to assist in creating Device classes for hardware that writes its own data e.g. an AreaDetector implementation, or a PandA writing motor encoder positions to file.
|
|
10
|
+
The `StandardDetector` is a simple compound device, with 2 standard components:
|
|
11
|
+
|
|
12
|
+
- `DetectorWriter` to handle data persistence, i/o and pass information about data to the RunEngine (usually an instance of :py:class:`HDFWriter`)
|
|
13
|
+
- `DetectorControl` with logic for arming and disarming the detector. This will be unique to the StandardDetector implementation.
|
|
14
|
+
|
|
15
|
+
Writing an AreaDetector StandardDetector
|
|
16
|
+
----------------------------------------
|
|
17
|
+
|
|
18
|
+
For an AreaDetector implementation of the StandardDetector, two entity objects which are subdevices of the `StandardDetector` are used to map to AreaDetector plugins:
|
|
19
|
+
|
|
20
|
+
- An NDPluginFile instance (for :py:class:`HDFWriter` an instance of :py:class:`NDFileHDF`)
|
|
21
|
+
- An :py:class:`ADBase` instance mapping to NDArray for the "driver" of the detector implementation
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Define a :py:class:`FooDriver` if the NDArray requires fields in addition to those on :py:class:`ADBase` to be exposed. It should extend :py:class:`ADBase`.
|
|
25
|
+
Enumeration fields should be named to prevent namespace collision, i.e. for a Signal named "TriggerSource" use the enum "FooTriggerSource"
|
|
26
|
+
|
|
27
|
+
.. literalinclude:: ../examples/foo_detector.py
|
|
28
|
+
:language: python
|
|
29
|
+
:pyobject: FooDriver
|
|
30
|
+
|
|
31
|
+
Define a :py:class:`FooController` with handling for converting the standard pattern of :py:meth:`ophyd_async.core.DetectorControl.arm` and :py:meth:`ophyd_async.core.DetectorControl.disarm` to required state of :py:class:`FooDriver` e.g. setting a compatible "FooTriggerSource" for a given `DetectorTrigger`, or raising an exception if incompatible with the `DetectorTrigger`.
|
|
32
|
+
|
|
33
|
+
The :py:meth:`ophyd_async.core.DetectorControl.get_deadtime` method is used when constructing sequence tables for hardware controlled scanning. Details on how to calculate the deadtime may be only available from technical manuals or otherwise complex. **If it requires fetching from signals, it is recommended to cache the value during the StandardDetector `prepare` method.**
|
|
34
|
+
|
|
35
|
+
.. literalinclude:: ../examples/foo_detector.py
|
|
36
|
+
:pyobject: FooController
|
|
37
|
+
|
|
38
|
+
:py:class:`FooDetector` ties the Driver, Controller and data persistence layer together. The example :py:class:`FooDetector` writes h5 files using the standard NDPlugin. It additionally supports the :py:class:`HasHints` protocol which is optional but recommended.
|
|
39
|
+
|
|
40
|
+
Its initialiser assumes the NSLS-II AreaDetector plugin EPICS address suffixes as defaults but allows overriding: **this pattern is recommended for consistency**.
|
|
41
|
+
If the :py:class:`FooDriver` signals that should be read as configuration, they should be added to the "config_sigs" passed to the super.
|
|
42
|
+
|
|
43
|
+
.. literalinclude:: ../examples/foo_detector.py
|
|
44
|
+
:pyobject: FooDetector
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
Writing a non-AreaDetector StandardDetector
|
|
48
|
+
-------------------------------------------
|
|
49
|
+
|
|
50
|
+
A non-AreaDetector `StandardDetector` should implement `DetectorControl` and `DetectorWriter` directly.
|
|
51
|
+
Here we construct a `DetectorControl` that co-ordinates signals on a PandA PositionCapture block - a child device "pcap" of the `StandardDetector` implementation, analogous to the :py:class:`FooDriver`.
|
|
52
|
+
|
|
53
|
+
.. literalinclude:: ../../src/ophyd_async/panda/_panda_controller.py
|
|
54
|
+
:pyobject: PandaPcapController
|
|
55
|
+
|
|
56
|
+
The PandA may write a number of fields, and the :py:class:`PandaHDFWriter` co-ordinates those, configures the filewriter and describes the data for the RunEngine.
|
|
57
|
+
|
|
58
|
+
.. literalinclude:: ../../src/ophyd_async/panda/writers/_hdf_writer.py
|
|
59
|
+
:pyobject: PandaHDFWriter
|
|
60
|
+
|
|
61
|
+
The PandA StandardDetector implementation simply ties the component parts and its child devices together.
|
|
62
|
+
|
|
63
|
+
.. literalinclude:: ../../src/ophyd_async/panda/_hdf_panda.py
|
|
64
|
+
:pyobject: HDFPanda
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
.. note::
|
|
2
|
+
|
|
3
|
+
Ophyd async is included on a provisional basis until the v1.0 release and
|
|
4
|
+
may change API on minor release numbers before then
|
|
5
|
+
|
|
6
|
+
Write Tests for Devices
|
|
7
|
+
=======================
|
|
8
|
+
|
|
9
|
+
Testing ophyd-async devices using tools like mocking, patching, and fixtures can become complicated very quickly. The library provides several utilities to make it easier.
|
|
10
|
+
|
|
11
|
+
Async Tests
|
|
12
|
+
-----------
|
|
13
|
+
|
|
14
|
+
`pytest-asyncio <https://github.com/pytest-dev/pytest-asyncio>`_ is required for async tests. It is should be included as a dev dependency of your project. Tests can either be decorated with ``@pytest.mark.asyncio`` or the project can be automatically configured to detect async tests.
|
|
15
|
+
|
|
16
|
+
.. code:: toml
|
|
17
|
+
|
|
18
|
+
# pyproject.toml
|
|
19
|
+
|
|
20
|
+
[tool.pytest.ini_options]
|
|
21
|
+
...
|
|
22
|
+
asyncio_mode = "auto"
|
|
23
|
+
|
|
24
|
+
Mock Backend
|
|
25
|
+
------------
|
|
26
|
+
|
|
27
|
+
Ophyd devices initialized with a mock backend behave in a similar way to mocks, without requiring you to mock out all the dependencies and internals. The `DeviceCollector` can initialize any number of devices, and their signals and sub-devices (recursively), with a mock backend.
|
|
28
|
+
|
|
29
|
+
.. literalinclude:: ../../tests/epics/demo/test_demo.py
|
|
30
|
+
:pyobject: mock_sensor
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
Mock Utility Functions
|
|
34
|
+
----------------------
|
|
35
|
+
|
|
36
|
+
Mock signals behave as simply as possible, holding a sensible default value when initialized and retaining any value (in memory) to which they are set. This model breaks down in the case of read-only signals, which cannot be set because there is an expectation of some external device setting them in the real world. There is a utility function, ``set_mock_value``, to mock-set values for mock signals, including read-only ones.
|
|
37
|
+
|
|
38
|
+
In addition this example also utilizes helper functions like ``assert_reading`` and ``assert_value`` to ensure the validity of device readings and values. For more information see: :doc:`API.core<../generated/ophyd_async.core>`
|
|
39
|
+
|
|
40
|
+
.. literalinclude:: ../../tests/epics/demo/test_demo.py
|
|
41
|
+
:pyobject: test_sensor_reading_shows_value
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
There are several other test utility functions:
|
|
45
|
+
|
|
46
|
+
Use ``callback_on_mock_put``, for hooking in logic when a mock value changes (e.g. because someone puts to it). This can be called directly, or used as a context, with the callbacks ending after exit.
|
|
47
|
+
|
|
48
|
+
.. literalinclude:: ../../tests/epics/demo/test_demo.py
|
|
49
|
+
:pyobject: test_mover_stopped
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
Testing a Device in a Plan with the RunEngine
|
|
53
|
+
---------------------------------------------
|
|
54
|
+
.. literalinclude:: ../../tests/epics/demo/test_demo.py
|
|
55
|
+
:pyobject: test_sensor_in_plan
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
This test verifies that the sim_sensor behaves as expected within a plan. The plan we use here is a ``count``, which takes a specified number of readings from the ``sim_sensor``. Since we set the ``repeat`` to two in this test, the sensor should emit two "event" documents along with "start", "stop" and "descriptor" documents. Finally, we use the helper function ``assert_emitted`` to confirm that the emitted documents match our expectations.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -57,7 +57,7 @@ and run the following:
|
|
|
57
57
|
- If ``connect=True`` (the default), then call `Device.connect` in parallel for
|
|
58
58
|
all top level Devices, waiting for up to ``timeout`` seconds. For example,
|
|
59
59
|
here we call ``asyncio.wait([det.connect(), samp.connect()])``
|
|
60
|
-
- If ``
|
|
60
|
+
- If ``mock=True`` is passed, then don't connect to PVs, but set Devices into
|
|
61
61
|
simulation mode
|
|
62
62
|
|
|
63
63
|
The Devices we create in this example are a "sample stage" with a couple of
|
|
@@ -20,6 +20,7 @@ dependencies = [
|
|
|
20
20
|
"event-model<1.21.0",
|
|
21
21
|
"p4p",
|
|
22
22
|
"pyyaml",
|
|
23
|
+
"colorlog",
|
|
23
24
|
]
|
|
24
25
|
dynamic = ["version"]
|
|
25
26
|
license.file = "LICENSE"
|
|
@@ -49,7 +50,7 @@ dev = [
|
|
|
49
50
|
"pre-commit",
|
|
50
51
|
"pydata-sphinx-theme>=0.12",
|
|
51
52
|
"pyepics>=3.4.2",
|
|
52
|
-
"pyside6==6.
|
|
53
|
+
"pyside6==6.7.0",
|
|
53
54
|
"pytest",
|
|
54
55
|
"pytest-asyncio",
|
|
55
56
|
"pytest-cov",
|
|
@@ -58,6 +59,7 @@ dev = [
|
|
|
58
59
|
"pytest-timeout",
|
|
59
60
|
"ruff",
|
|
60
61
|
"sphinx-autobuild",
|
|
62
|
+
"sphinxcontrib-mermaid",
|
|
61
63
|
"sphinx-copybutton",
|
|
62
64
|
"sphinx-design",
|
|
63
65
|
"tox-direct",
|
|
@@ -84,7 +86,6 @@ write_to = "src/ophyd_async/_version.py"
|
|
|
84
86
|
addopts = """
|
|
85
87
|
--tb=native -vv --strict-markers --doctest-modules
|
|
86
88
|
--doctest-glob="*.rst" --doctest-glob="*.md" --ignore=docs/examples
|
|
87
|
-
--cov=src/ophyd_async --cov-report term --cov-report xml:cov.xml
|
|
88
89
|
"""
|
|
89
90
|
# https://iscinumpy.gitlab.io/post/bound-version-constraints/#watch-for-warnings
|
|
90
91
|
filterwarnings = "error"
|
|
@@ -24,24 +24,37 @@ from .device_save_loader import (
|
|
|
24
24
|
walk_rw_signals,
|
|
25
25
|
)
|
|
26
26
|
from .flyer import HardwareTriggeredFlyable, TriggerLogic
|
|
27
|
+
from .mock_signal_backend import (
|
|
28
|
+
MockSignalBackend,
|
|
29
|
+
)
|
|
30
|
+
from .mock_signal_utils import (
|
|
31
|
+
assert_mock_put_called_with,
|
|
32
|
+
callback_on_mock_put,
|
|
33
|
+
mock_puts_blocked,
|
|
34
|
+
reset_mock_put_calls,
|
|
35
|
+
set_mock_put_proceeds,
|
|
36
|
+
set_mock_value,
|
|
37
|
+
set_mock_values,
|
|
38
|
+
)
|
|
27
39
|
from .signal import (
|
|
28
40
|
Signal,
|
|
29
41
|
SignalR,
|
|
30
42
|
SignalRW,
|
|
31
43
|
SignalW,
|
|
32
44
|
SignalX,
|
|
45
|
+
assert_configuration,
|
|
46
|
+
assert_emitted,
|
|
47
|
+
assert_reading,
|
|
48
|
+
assert_value,
|
|
33
49
|
observe_value,
|
|
34
50
|
set_and_wait_for_value,
|
|
35
|
-
|
|
36
|
-
set_sim_put_proceeds,
|
|
37
|
-
set_sim_value,
|
|
38
|
-
soft_signal_r_and_backend,
|
|
51
|
+
soft_signal_r_and_setter,
|
|
39
52
|
soft_signal_rw,
|
|
40
53
|
wait_for_value,
|
|
41
54
|
)
|
|
42
55
|
from .signal_backend import SignalBackend
|
|
43
|
-
from .
|
|
44
|
-
from .standard_readable import StandardReadable
|
|
56
|
+
from .soft_signal_backend import SoftSignalBackend
|
|
57
|
+
from .standard_readable import ConfigSignal, HintedSignal, StandardReadable
|
|
45
58
|
from .utils import (
|
|
46
59
|
DEFAULT_TIMEOUT,
|
|
47
60
|
Callback,
|
|
@@ -55,9 +68,15 @@ from .utils import (
|
|
|
55
68
|
)
|
|
56
69
|
|
|
57
70
|
__all__ = [
|
|
71
|
+
"assert_mock_put_called_with",
|
|
72
|
+
"callback_on_mock_put",
|
|
73
|
+
"mock_puts_blocked",
|
|
74
|
+
"set_mock_values",
|
|
75
|
+
"reset_mock_put_calls",
|
|
58
76
|
"SignalBackend",
|
|
59
|
-
"
|
|
77
|
+
"SoftSignalBackend",
|
|
60
78
|
"DetectorControl",
|
|
79
|
+
"MockSignalBackend",
|
|
61
80
|
"DetectorTrigger",
|
|
62
81
|
"DetectorWriter",
|
|
63
82
|
"StandardDetector",
|
|
@@ -69,13 +88,12 @@ __all__ = [
|
|
|
69
88
|
"SignalW",
|
|
70
89
|
"SignalRW",
|
|
71
90
|
"SignalX",
|
|
72
|
-
"
|
|
91
|
+
"soft_signal_r_and_setter",
|
|
73
92
|
"soft_signal_rw",
|
|
74
93
|
"observe_value",
|
|
75
94
|
"set_and_wait_for_value",
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"set_sim_value",
|
|
95
|
+
"set_mock_put_proceeds",
|
|
96
|
+
"set_mock_value",
|
|
79
97
|
"wait_for_value",
|
|
80
98
|
"AsyncStatus",
|
|
81
99
|
"DirectoryInfo",
|
|
@@ -84,6 +102,8 @@ __all__ = [
|
|
|
84
102
|
"ShapeProvider",
|
|
85
103
|
"StaticDirectoryProvider",
|
|
86
104
|
"StandardReadable",
|
|
105
|
+
"ConfigSignal",
|
|
106
|
+
"HintedSignal",
|
|
87
107
|
"TriggerInfo",
|
|
88
108
|
"TriggerLogic",
|
|
89
109
|
"HardwareTriggeredFlyable",
|
|
@@ -103,4 +123,8 @@ __all__ = [
|
|
|
103
123
|
"walk_rw_signals",
|
|
104
124
|
"load_device",
|
|
105
125
|
"save_device",
|
|
126
|
+
"assert_reading",
|
|
127
|
+
"assert_value",
|
|
128
|
+
"assert_configuration",
|
|
129
|
+
"assert_emitted",
|
|
106
130
|
]
|
|
@@ -21,7 +21,9 @@ class AsyncStatus(Status):
|
|
|
21
21
|
self.task = awaitable
|
|
22
22
|
else:
|
|
23
23
|
self.task = asyncio.create_task(awaitable) # type: ignore
|
|
24
|
+
|
|
24
25
|
self.task.add_done_callback(self._run_callbacks)
|
|
26
|
+
|
|
25
27
|
self._callbacks = cast(List[Callback[Status]], [])
|
|
26
28
|
self._watchers = watchers
|
|
27
29
|
|