ophyd-async 0.6.0__tar.gz → 0.7.0__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.6.0 → ophyd_async-0.7.0}/PKG-INFO +5 -1
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/examples/foo_detector.py +2 -2
- ophyd_async-0.7.0/docs/examples/tango_demo.py +54 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/decisions/0007-subpackage-structure.md +1 -1
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/how-to/make-a-standard-detector.rst +5 -5
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/pyproject.toml +3 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/_version.py +2 -2
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/__init__.py +4 -4
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_detector.py +74 -37
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_device.py +6 -1
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_flyer.py +5 -20
- ophyd_async-0.7.0/src/ophyd_async/core/_table.py +146 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adaravis/_aravis_controller.py +4 -4
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adcore/_core_logic.py +2 -2
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adkinetix/_kinetix_controller.py +3 -3
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adpilatus/_pilatus_controller.py +5 -3
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adsimdetector/_sim.py +1 -1
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adsimdetector/_sim_controller.py +3 -3
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/advimba/_vimba_controller.py +3 -3
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/eiger/_eiger_controller.py +3 -3
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/fastcs/panda/_block.py +7 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/fastcs/panda/_control.py +2 -2
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/fastcs/panda/_table.py +3 -37
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/fastcs/panda/_trigger.py +3 -3
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/fastcs/panda/_writer.py +2 -2
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/plan_stubs/_fly.py +1 -3
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/sim/demo/_pattern_detector/_pattern_detector_controller.py +5 -3
- ophyd_async-0.7.0/src/ophyd_async/tango/__init__.py +45 -0
- ophyd_async-0.7.0/src/ophyd_async/tango/base_devices/__init__.py +4 -0
- ophyd_async-0.7.0/src/ophyd_async/tango/base_devices/_base_device.py +225 -0
- ophyd_async-0.7.0/src/ophyd_async/tango/base_devices/_tango_readable.py +33 -0
- ophyd_async-0.7.0/src/ophyd_async/tango/demo/__init__.py +12 -0
- ophyd_async-0.7.0/src/ophyd_async/tango/demo/_counter.py +37 -0
- ophyd_async-0.7.0/src/ophyd_async/tango/demo/_detector.py +42 -0
- ophyd_async-0.7.0/src/ophyd_async/tango/demo/_mover.py +77 -0
- ophyd_async-0.7.0/src/ophyd_async/tango/demo/_tango/__init__.py +3 -0
- ophyd_async-0.7.0/src/ophyd_async/tango/demo/_tango/_servers.py +108 -0
- ophyd_async-0.7.0/src/ophyd_async/tango/signal/__init__.py +39 -0
- ophyd_async-0.7.0/src/ophyd_async/tango/signal/_signal.py +223 -0
- ophyd_async-0.7.0/src/ophyd_async/tango/signal/_tango_transport.py +764 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async.egg-info/PKG-INFO +5 -1
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async.egg-info/SOURCES.txt +16 -2
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async.egg-info/requires.txt +5 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/system_tests/epics/eiger/test_eiger_system.py +1 -1
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/conftest.py +10 -2
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_device_collector.py +10 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_flyer.py +200 -61
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/adaravis/test_aravis.py +3 -11
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/adcore/test_drivers.py +4 -4
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/adcore/test_scans.py +9 -6
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/adkinetix/test_kinetix.py +4 -11
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/adpilatus/test_pilatus.py +40 -27
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/adsimdetector/test_sim.py +95 -95
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/advimba/test_vimba.py +6 -11
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/conftest.py +22 -4
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/eiger/test_eiger_controller.py +4 -4
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/eiger/test_eiger_detector.py +1 -1
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/fastcs/panda/test_hdf_panda.py +3 -119
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/fastcs/panda/test_panda_control.py +3 -3
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/fastcs/panda/test_panda_utils.py +1 -0
- ophyd_async-0.7.0/tests/fastcs/panda/test_table.py +385 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/fastcs/panda/test_trigger.py +21 -8
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/plan_stubs/test_fly.py +16 -12
- ophyd_async-0.7.0/tests/tango/test_base_device.py +400 -0
- ophyd_async-0.7.0/tests/tango/test_tango_signals.py +775 -0
- ophyd_async-0.7.0/tests/tango/test_tango_transport.py +854 -0
- ophyd_async-0.6.0/src/ophyd_async/core/_table.py +0 -63
- ophyd_async-0.6.0/tests/epics/adpilatus/test_pilatus_controller.py +0 -46
- ophyd_async-0.6.0/tests/epics/adsimdetector/test_adsim_controller.py +0 -32
- ophyd_async-0.6.0/tests/fastcs/panda/test_table.py +0 -232
- ophyd_async-0.6.0/tests/sim/demo/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.codecov.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.copier-answers.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.devcontainer/devcontainer.json +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.git-blame-ignore-revs +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/CONTRIBUTING.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/ISSUE_TEMPLATE/issue.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/actions/install_requirements/action.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/dependabot.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/pages/index.html +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/pages/make_switcher.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/workflows/_check.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/workflows/_dist.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/workflows/_docs.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/workflows/_pypi.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/workflows/_release.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/workflows/_test.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/workflows/_tox.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/workflows/ci.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.github/workflows/periodic.yml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.gitignore +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.mailmap +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/.pre-commit-config.yaml +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/Dockerfile +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/LICENSE +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/README.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/_api.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/_templates/custom-module-template.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/conf.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/examples/epics_demo.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/decisions/0001-record-architecture-decisions.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/decisions/0002-switched-to-python-copier-template.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/decisions/0003-ophyd-async-migration.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/decisions/0004-repository-structure.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/decisions/0005-respect-black-line-length.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/decisions/0006-procedural-device-definitions.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/decisions/COPYME +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/decisions.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/design-goals.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/event-loop-choice.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/flyscanning.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/genindex.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/how-to/choose-interfaces-for-devices.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/how-to/compound-devices.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/how-to/contribute.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/how-to/make-a-simple-device.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/how-to/write-tests-for-devices.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/how-to.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/images/bluesky_ophyd_epics_devices_logo.svg +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/images/bluesky_ophyd_logo.svg +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/images/ophyd_favicon.svg +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/index.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/reference.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/tutorials/installation.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/tutorials/using-existing-devices.rst +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/tutorials.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/setup.cfg +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/__main__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_device_save_loader.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_hdf_dataset.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_log.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_mock_signal_backend.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_mock_signal_utils.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_protocol.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_providers.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_readable.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_signal.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_signal_backend.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_soft_signal_backend.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_status.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/core/_utils.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adaravis/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adaravis/_aravis.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adaravis/_aravis_io.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adcore/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adcore/_core_io.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adcore/_hdf_writer.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adcore/_single_trigger.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adcore/_utils.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adkinetix/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adkinetix/_kinetix.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adkinetix/_kinetix_io.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adpilatus/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adpilatus/_pilatus.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adpilatus/_pilatus_io.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adsimdetector/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/advimba/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/advimba/_vimba.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/advimba/_vimba_io.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/demo/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/demo/_mover.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/demo/_sensor.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/demo/mover.db +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/demo/sensor.db +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/eiger/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/eiger/_eiger.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/eiger/_eiger_io.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/eiger/_odin_io.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/motor.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/pvi/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/pvi/_pvi.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/signal/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/signal/_aioca.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/signal/_common.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/signal/_epics_transport.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/signal/_p4p.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/signal/_signal.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/fastcs/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/fastcs/odin/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/fastcs/panda/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/fastcs/panda/_hdf_panda.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/fastcs/panda/_utils.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/plan_stubs/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/plan_stubs/_ensure_connected.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/plan_stubs/_nd_attributes.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/sim/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/sim/demo/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/sim/demo/_pattern_detector/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/sim/demo/_pattern_detector/_pattern_detector.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/sim/demo/_pattern_detector/_pattern_detector_writer.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/sim/demo/_pattern_detector/_pattern_generator.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/sim/demo/_sim_motor.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/sim/testing/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async.egg-info/dependency_links.txt +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async.egg-info/entry_points.txt +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async.egg-info/top_level.txt +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/system_tests/epics/eiger/README.md +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/system_tests/epics/eiger/start_iocs_and_run_tests.sh +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_device.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_device_save_loader.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_log.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_mock_signal_backend.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_protocol.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_providers.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_readable.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_signal.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_soft_signal_backend.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_status.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_subset_enum.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_utils.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/core/test_watchable_async_status.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/adcore/test_single_trigger.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/adcore/test_writers.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/demo/test_demo.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/eiger/test_odin_io.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/pvi/test_pvi.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/signal/test_common.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/signal/test_records.db +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/signal/test_signals.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/test_areadetector_subclass_naming.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/epics/test_motor.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/fastcs/panda/db/panda.db +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/fastcs/panda/test_panda_connect.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/fastcs/panda/test_writer.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/plan_stubs/test_ensure_connected.py +0 -0
- {ophyd_async-0.6.0/src/ophyd_async/tango → ophyd_async-0.7.0/tests/sim}/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/sim/conftest.py +0 -0
- {ophyd_async-0.6.0/tests/sim → ophyd_async-0.7.0/tests/sim/demo}/__init__.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/sim/demo/test_sim_motor.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/sim/test_pattern_generator.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/sim/test_sim_detector.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/sim/test_sim_writer.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/sim/test_streaming_plan.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/test_cli.py +0 -0
- {ophyd_async-0.6.0 → ophyd_async-0.7.0}/tests/test_data/test_yaml_save.yml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ophyd-async
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
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
|
|
@@ -57,10 +57,13 @@ Provides-Extra: pva
|
|
|
57
57
|
Requires-Dist: p4p; extra == "pva"
|
|
58
58
|
Provides-Extra: sim
|
|
59
59
|
Requires-Dist: h5py; extra == "sim"
|
|
60
|
+
Provides-Extra: tango
|
|
61
|
+
Requires-Dist: pytango>=10.0.0; extra == "tango"
|
|
60
62
|
Provides-Extra: dev
|
|
61
63
|
Requires-Dist: ophyd_async[pva]; extra == "dev"
|
|
62
64
|
Requires-Dist: ophyd_async[sim]; extra == "dev"
|
|
63
65
|
Requires-Dist: ophyd_async[ca]; extra == "dev"
|
|
66
|
+
Requires-Dist: ophyd_async[tango]; extra == "dev"
|
|
64
67
|
Requires-Dist: black; extra == "dev"
|
|
65
68
|
Requires-Dist: flake8; extra == "dev"
|
|
66
69
|
Requires-Dist: flake8-isort; extra == "dev"
|
|
@@ -83,6 +86,7 @@ Requires-Dist: pytest; extra == "dev"
|
|
|
83
86
|
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
84
87
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
85
88
|
Requires-Dist: pytest-faulthandler; extra == "dev"
|
|
89
|
+
Requires-Dist: pytest-forked; extra == "dev"
|
|
86
90
|
Requires-Dist: pytest-rerunfailures; extra == "dev"
|
|
87
91
|
Requires-Dist: pytest-timeout; extra == "dev"
|
|
88
92
|
Requires-Dist: ruff; extra == "dev"
|
|
@@ -4,7 +4,7 @@ from bluesky.protocols import HasHints, Hints
|
|
|
4
4
|
|
|
5
5
|
from ophyd_async.core import (
|
|
6
6
|
AsyncStatus,
|
|
7
|
-
|
|
7
|
+
DetectorController,
|
|
8
8
|
DetectorTrigger,
|
|
9
9
|
PathProvider,
|
|
10
10
|
StandardDetector,
|
|
@@ -19,7 +19,7 @@ class FooDriver(adcore.ADBaseIO):
|
|
|
19
19
|
super().__init__(prefix, name)
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
class FooController(
|
|
22
|
+
class FooController(DetectorController):
|
|
23
23
|
def __init__(self, driver: FooDriver) -> None:
|
|
24
24
|
self._drv = driver
|
|
25
25
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
import bluesky.plan_stubs as bps
|
|
4
|
+
import bluesky.plans as bp
|
|
5
|
+
from bluesky import RunEngine
|
|
6
|
+
|
|
7
|
+
from ophyd_async.tango.demo import (
|
|
8
|
+
DemoCounter,
|
|
9
|
+
DemoMover,
|
|
10
|
+
TangoDetector,
|
|
11
|
+
)
|
|
12
|
+
from tango.test_context import MultiDeviceTestContext
|
|
13
|
+
|
|
14
|
+
content = (
|
|
15
|
+
{
|
|
16
|
+
"class": DemoMover,
|
|
17
|
+
"devices": [{"name": "demo/motor/1"}],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"class": DemoCounter,
|
|
21
|
+
"devices": [{"name": "demo/counter/1"}, {"name": "demo/counter/2"}],
|
|
22
|
+
},
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
tango_context = MultiDeviceTestContext(content)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def main():
|
|
29
|
+
with tango_context:
|
|
30
|
+
detector = TangoDetector(
|
|
31
|
+
trl="",
|
|
32
|
+
name="detector",
|
|
33
|
+
counters_kwargs={"prefix": "demo/counter/", "count": 2},
|
|
34
|
+
mover_kwargs={"trl": "demo/motor/1"},
|
|
35
|
+
)
|
|
36
|
+
await detector.connect()
|
|
37
|
+
|
|
38
|
+
RE = RunEngine()
|
|
39
|
+
|
|
40
|
+
RE(bps.read(detector))
|
|
41
|
+
RE(bps.mv(detector, 0))
|
|
42
|
+
RE(bp.count(list(detector.counters.values())))
|
|
43
|
+
|
|
44
|
+
set_status = detector.set(1.0)
|
|
45
|
+
await asyncio.sleep(0.1)
|
|
46
|
+
stop_status = detector.stop()
|
|
47
|
+
await set_status
|
|
48
|
+
await stop_status
|
|
49
|
+
assert all([set_status.done, stop_status.done])
|
|
50
|
+
assert all([set_status.success, stop_status.success])
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if __name__ == "__main__":
|
|
54
|
+
asyncio.run(main())
|
{ophyd_async-0.6.0 → ophyd_async-0.7.0}/docs/explanations/decisions/0007-subpackage-structure.md
RENAMED
|
@@ -31,7 +31,7 @@ There will be a flat public namespace under core, with contents reimported from
|
|
|
31
31
|
- `_signal.py` for `Signal`, `SignalBackend`, `observe_signal`, etc.
|
|
32
32
|
- `_mock.py` for `MockSignalBackend`, `get_mock_put`, etc.
|
|
33
33
|
- `_readable.py` for `StandardReadable`, `ConfigSignal`, `HintedSignal`, etc.
|
|
34
|
-
- `_detector.py` for `StandardDetector`, `DetectorWriter`, `
|
|
34
|
+
- `_detector.py` for `StandardDetector`, `DetectorWriter`, `DetectorController`, `TriggerInfo`, etc.
|
|
35
35
|
- `_flyer.py` for `StandardFlyer`, `FlyerControl`, etc.
|
|
36
36
|
|
|
37
37
|
There are some renames that will be required, e.g. `HardwareTriggeredFlyable` -> `StandardFlyer`
|
|
@@ -10,7 +10,7 @@ Make a StandardDetector
|
|
|
10
10
|
The `StandardDetector` is a simple compound device, with 2 standard components:
|
|
11
11
|
|
|
12
12
|
- `DetectorWriter` to handle data persistence, i/o and pass information about data to the RunEngine (usually an instance of :py:class:`ADHDFWriter`)
|
|
13
|
-
- `
|
|
13
|
+
- `DetectorController` with logic for arming and disarming the detector. This will be unique to the StandardDetector implementation.
|
|
14
14
|
|
|
15
15
|
Writing an AreaDetector StandardDetector
|
|
16
16
|
----------------------------------------
|
|
@@ -28,9 +28,9 @@ Enumeration fields should be named to prevent namespace collision, i.e. for a Si
|
|
|
28
28
|
:language: python
|
|
29
29
|
:pyobject: FooDriver
|
|
30
30
|
|
|
31
|
-
Define a :py:class:`FooController` with handling for converting the standard pattern of :py:meth:`ophyd_async.core.
|
|
31
|
+
Define a :py:class:`FooController` with handling for converting the standard pattern of :py:meth:`ophyd_async.core.DetectorController.arm` and :py:meth:`ophyd_async.core.DetectorController.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
32
|
|
|
33
|
-
The :py:meth:`ophyd_async.core.
|
|
33
|
+
The :py:meth:`ophyd_async.core.DetectorController.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
34
|
|
|
35
35
|
.. literalinclude:: ../examples/foo_detector.py
|
|
36
36
|
:pyobject: FooController
|
|
@@ -47,8 +47,8 @@ If the :py:class:`FooDriver` signals that should be read as configuration, they
|
|
|
47
47
|
Writing a non-AreaDetector StandardDetector
|
|
48
48
|
-------------------------------------------
|
|
49
49
|
|
|
50
|
-
A non-AreaDetector `StandardDetector` should implement `
|
|
51
|
-
Here we construct a `
|
|
50
|
+
A non-AreaDetector `StandardDetector` should implement `DetectorController` and `DetectorWriter` directly.
|
|
51
|
+
Here we construct a `DetectorController` that co-ordinates signals on a PandA PositionCapture block - a child device "pcap" of the `StandardDetector` implementation, analogous to the :py:class:`FooDriver`.
|
|
52
52
|
|
|
53
53
|
.. literalinclude:: ../../src/ophyd_async/fastcs/panda/_control.py
|
|
54
54
|
:pyobject: PandaPcapController
|
|
@@ -33,10 +33,12 @@ requires-python = ">=3.10"
|
|
|
33
33
|
ca = ["aioca>=1.6"]
|
|
34
34
|
pva = ["p4p"]
|
|
35
35
|
sim = ["h5py"]
|
|
36
|
+
tango = ["pytango>=10.0.0"]
|
|
36
37
|
dev = [
|
|
37
38
|
"ophyd_async[pva]",
|
|
38
39
|
"ophyd_async[sim]",
|
|
39
40
|
"ophyd_async[ca]",
|
|
41
|
+
"ophyd_async[tango]",
|
|
40
42
|
"black",
|
|
41
43
|
"flake8",
|
|
42
44
|
"flake8-isort",
|
|
@@ -59,6 +61,7 @@ dev = [
|
|
|
59
61
|
"pytest-asyncio",
|
|
60
62
|
"pytest-cov",
|
|
61
63
|
"pytest-faulthandler",
|
|
64
|
+
"pytest-forked",
|
|
62
65
|
"pytest-rerunfailures",
|
|
63
66
|
"pytest-timeout",
|
|
64
67
|
"ruff",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from ._detector import (
|
|
2
|
-
|
|
2
|
+
DetectorController,
|
|
3
3
|
DetectorTrigger,
|
|
4
4
|
DetectorWriter,
|
|
5
5
|
StandardDetector,
|
|
@@ -16,7 +16,7 @@ from ._device_save_loader import (
|
|
|
16
16
|
set_signal_values,
|
|
17
17
|
walk_rw_signals,
|
|
18
18
|
)
|
|
19
|
-
from ._flyer import
|
|
19
|
+
from ._flyer import FlyerController, StandardFlyer
|
|
20
20
|
from ._hdf_dataset import HDFDataset, HDFFile
|
|
21
21
|
from ._log import config_ophyd_async_logging
|
|
22
22
|
from ._mock_signal_backend import MockSignalBackend
|
|
@@ -85,7 +85,7 @@ from ._utils import (
|
|
|
85
85
|
)
|
|
86
86
|
|
|
87
87
|
__all__ = [
|
|
88
|
-
"
|
|
88
|
+
"DetectorController",
|
|
89
89
|
"DetectorTrigger",
|
|
90
90
|
"DetectorWriter",
|
|
91
91
|
"StandardDetector",
|
|
@@ -102,7 +102,7 @@ __all__ = [
|
|
|
102
102
|
"set_signal_values",
|
|
103
103
|
"walk_rw_signals",
|
|
104
104
|
"StandardFlyer",
|
|
105
|
-
"
|
|
105
|
+
"FlyerController",
|
|
106
106
|
"HDFDataset",
|
|
107
107
|
"HDFFile",
|
|
108
108
|
"config_ophyd_async_logging",
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import time
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
|
-
from collections.abc import AsyncGenerator, AsyncIterator, Callable, Sequence
|
|
6
|
+
from collections.abc import AsyncGenerator, AsyncIterator, Callable, Iterator, Sequence
|
|
7
7
|
from enum import Enum
|
|
8
|
+
from functools import cached_property
|
|
8
9
|
from typing import (
|
|
9
10
|
Generic,
|
|
10
11
|
)
|
|
@@ -20,7 +21,7 @@ from bluesky.protocols import (
|
|
|
20
21
|
WritesStreamAssets,
|
|
21
22
|
)
|
|
22
23
|
from event_model import DataKey
|
|
23
|
-
from pydantic import BaseModel, Field
|
|
24
|
+
from pydantic import BaseModel, Field, NonNegativeInt, computed_field
|
|
24
25
|
|
|
25
26
|
from ._device import Device
|
|
26
27
|
from ._protocol import AsyncConfigurable, AsyncReadable
|
|
@@ -45,8 +46,16 @@ class DetectorTrigger(str, Enum):
|
|
|
45
46
|
class TriggerInfo(BaseModel):
|
|
46
47
|
"""Minimal set of information required to setup triggering on a detector"""
|
|
47
48
|
|
|
48
|
-
#: Number of triggers that will be sent, 0 means infinite
|
|
49
|
-
|
|
49
|
+
#: Number of triggers that will be sent, (0 means infinite) Can be:
|
|
50
|
+
# - A single integer or
|
|
51
|
+
# - A list of integers for multiple triggers
|
|
52
|
+
# Example for tomography: TriggerInfo(number=[2,3,100,3])
|
|
53
|
+
#: This would trigger:
|
|
54
|
+
#: - 2 times for dark field images
|
|
55
|
+
#: - 3 times for initial flat field images
|
|
56
|
+
#: - 100 times for projections
|
|
57
|
+
#: - 3 times for final flat field images
|
|
58
|
+
number_of_triggers: NonNegativeInt | list[NonNegativeInt]
|
|
50
59
|
#: Sort of triggers that will be sent
|
|
51
60
|
trigger: DetectorTrigger = Field(default=DetectorTrigger.internal)
|
|
52
61
|
#: What is the minimum deadtime between triggers
|
|
@@ -60,13 +69,18 @@ class TriggerInfo(BaseModel):
|
|
|
60
69
|
#: e.g. if num=10 and multiplier=5 then the detector will take 10 frames,
|
|
61
70
|
#: but publish 2 indices, and describe() will show a shape of (5, h, w)
|
|
62
71
|
multiplier: int = 1
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
|
|
73
|
+
@computed_field
|
|
74
|
+
@cached_property
|
|
75
|
+
def total_number_of_triggers(self) -> int:
|
|
76
|
+
return (
|
|
77
|
+
sum(self.number_of_triggers)
|
|
78
|
+
if isinstance(self.number_of_triggers, list)
|
|
79
|
+
else self.number_of_triggers
|
|
80
|
+
)
|
|
67
81
|
|
|
68
82
|
|
|
69
|
-
class
|
|
83
|
+
class DetectorController(ABC):
|
|
70
84
|
"""
|
|
71
85
|
Classes implementing this interface should hold the logic for
|
|
72
86
|
arming and disarming a detector
|
|
@@ -167,7 +181,7 @@ class StandardDetector(
|
|
|
167
181
|
|
|
168
182
|
def __init__(
|
|
169
183
|
self,
|
|
170
|
-
controller:
|
|
184
|
+
controller: DetectorController,
|
|
171
185
|
writer: DetectorWriter,
|
|
172
186
|
config_sigs: Sequence[SignalR] = (),
|
|
173
187
|
name: str = "",
|
|
@@ -192,14 +206,18 @@ class StandardDetector(
|
|
|
192
206
|
# For kickoff
|
|
193
207
|
self._watchers: list[Callable] = []
|
|
194
208
|
self._fly_status: WatchableAsyncStatus | None = None
|
|
195
|
-
self._fly_start: float
|
|
196
|
-
self.
|
|
197
|
-
|
|
198
|
-
|
|
209
|
+
self._fly_start: float | None = None
|
|
210
|
+
self._frames_to_complete: int = 0
|
|
211
|
+
# Represents the total number of frames that will have been completed at the
|
|
212
|
+
# end of the next `complete`.
|
|
213
|
+
self._completable_frames: int = 0
|
|
214
|
+
self._number_of_triggers_iter: Iterator[int] | None = None
|
|
215
|
+
self._initial_frame: int = 0
|
|
216
|
+
|
|
199
217
|
super().__init__(name)
|
|
200
218
|
|
|
201
219
|
@property
|
|
202
|
-
def controller(self) ->
|
|
220
|
+
def controller(self) -> DetectorController:
|
|
203
221
|
return self._controller
|
|
204
222
|
|
|
205
223
|
@property
|
|
@@ -208,7 +226,7 @@ class StandardDetector(
|
|
|
208
226
|
|
|
209
227
|
@AsyncStatus.wrap
|
|
210
228
|
async def stage(self) -> None:
|
|
211
|
-
# Disarm the detector, stop
|
|
229
|
+
# Disarm the detector, stop file writing.
|
|
212
230
|
await self._check_config_sigs()
|
|
213
231
|
await asyncio.gather(self.writer.close(), self.controller.disarm())
|
|
214
232
|
self._trigger_info = None
|
|
@@ -251,7 +269,7 @@ class StandardDetector(
|
|
|
251
269
|
if self._trigger_info is None:
|
|
252
270
|
await self.prepare(
|
|
253
271
|
TriggerInfo(
|
|
254
|
-
|
|
272
|
+
number_of_triggers=1,
|
|
255
273
|
trigger=DetectorTrigger.internal,
|
|
256
274
|
deadtime=None,
|
|
257
275
|
livetime=None,
|
|
@@ -301,8 +319,12 @@ class StandardDetector(
|
|
|
301
319
|
f"but trigger logic provides only {value.deadtime}s"
|
|
302
320
|
)
|
|
303
321
|
self._trigger_info = value
|
|
322
|
+
self._number_of_triggers_iter = iter(
|
|
323
|
+
self._trigger_info.number_of_triggers
|
|
324
|
+
if isinstance(self._trigger_info.number_of_triggers, list)
|
|
325
|
+
else [self._trigger_info.number_of_triggers]
|
|
326
|
+
)
|
|
304
327
|
self._initial_frame = await self.writer.get_indices_written()
|
|
305
|
-
self._last_frame = self._initial_frame + self._trigger_info.number
|
|
306
328
|
self._describe, _ = await asyncio.gather(
|
|
307
329
|
self.writer.open(value.multiplier), self.controller.prepare(value)
|
|
308
330
|
)
|
|
@@ -312,35 +334,50 @@ class StandardDetector(
|
|
|
312
334
|
|
|
313
335
|
@AsyncStatus.wrap
|
|
314
336
|
async def kickoff(self):
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
337
|
+
if self._trigger_info is None or self._number_of_triggers_iter is None:
|
|
338
|
+
raise RuntimeError("Prepare must be called before kickoff!")
|
|
339
|
+
try:
|
|
340
|
+
self._frames_to_complete = next(self._number_of_triggers_iter)
|
|
341
|
+
self._completable_frames += self._frames_to_complete
|
|
342
|
+
except StopIteration as err:
|
|
343
|
+
raise RuntimeError(
|
|
344
|
+
f"Kickoff called more than the configured number of "
|
|
345
|
+
f"{self._trigger_info.total_number_of_triggers} iteration(s)!"
|
|
346
|
+
) from err
|
|
319
347
|
|
|
320
348
|
@WatchableAsyncStatus.wrap
|
|
321
349
|
async def complete(self):
|
|
322
350
|
assert self._trigger_info
|
|
323
|
-
|
|
351
|
+
indices_written = self.writer.observe_indices_written(
|
|
324
352
|
self._trigger_info.frame_timeout
|
|
325
353
|
or (
|
|
326
354
|
DEFAULT_TIMEOUT
|
|
327
355
|
+ (self._trigger_info.livetime or 0)
|
|
328
356
|
+ (self._trigger_info.deadtime or 0)
|
|
329
357
|
)
|
|
330
|
-
)
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
358
|
+
)
|
|
359
|
+
try:
|
|
360
|
+
async for index in indices_written:
|
|
361
|
+
yield WatcherUpdate(
|
|
362
|
+
name=self.name,
|
|
363
|
+
current=index,
|
|
364
|
+
initial=self._initial_frame,
|
|
365
|
+
target=self._frames_to_complete,
|
|
366
|
+
unit="",
|
|
367
|
+
precision=0,
|
|
368
|
+
time_elapsed=time.monotonic() - self._fly_start
|
|
369
|
+
if self._fly_start
|
|
370
|
+
else None,
|
|
371
|
+
)
|
|
372
|
+
if index >= self._frames_to_complete:
|
|
373
|
+
break
|
|
374
|
+
finally:
|
|
375
|
+
await indices_written.aclose()
|
|
376
|
+
if self._completable_frames >= self._trigger_info.total_number_of_triggers:
|
|
377
|
+
self._completable_frames = 0
|
|
378
|
+
self._frames_to_complete = 0
|
|
379
|
+
self._number_of_triggers_iter = None
|
|
380
|
+
await self.controller.wait_for_idle()
|
|
344
381
|
|
|
345
382
|
async def describe_collect(self) -> dict[str, DataKey]:
|
|
346
383
|
return self._describe
|
|
@@ -12,7 +12,7 @@ from typing import (
|
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
from bluesky.protocols import HasName
|
|
15
|
-
from bluesky.run_engine import call_in_bluesky_event_loop
|
|
15
|
+
from bluesky.run_engine import call_in_bluesky_event_loop, in_bluesky_event_loop
|
|
16
16
|
|
|
17
17
|
from ._utils import DEFAULT_TIMEOUT, NotConnected, wait_for_connection
|
|
18
18
|
|
|
@@ -224,6 +224,11 @@ class DeviceCollector:
|
|
|
224
224
|
await self._on_exit()
|
|
225
225
|
|
|
226
226
|
def __exit__(self, type_, value, traceback):
|
|
227
|
+
if in_bluesky_event_loop():
|
|
228
|
+
raise RuntimeError(
|
|
229
|
+
"Cannot use DeviceConnector inside a plan, instead use "
|
|
230
|
+
"`yield from ophyd_async.plan_stubs.ensure_connected(device)`"
|
|
231
|
+
)
|
|
227
232
|
self._objects_on_exit = self._caller_locals()
|
|
228
233
|
try:
|
|
229
234
|
fut = call_in_bluesky_event_loop(self._on_exit())
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from collections.abc import Sequence
|
|
3
2
|
from typing import Generic
|
|
4
3
|
|
|
5
|
-
from bluesky.protocols import Flyable, Preparable,
|
|
6
|
-
from event_model import DataKey
|
|
4
|
+
from bluesky.protocols import Flyable, Preparable, Stageable
|
|
7
5
|
|
|
8
6
|
from ._device import Device
|
|
9
|
-
from ._signal import SignalR
|
|
10
7
|
from ._status import AsyncStatus
|
|
11
|
-
from ._utils import T
|
|
8
|
+
from ._utils import T
|
|
12
9
|
|
|
13
10
|
|
|
14
|
-
class
|
|
11
|
+
class FlyerController(ABC, Generic[T]):
|
|
15
12
|
@abstractmethod
|
|
16
13
|
async def prepare(self, value: T):
|
|
17
14
|
"""Move to the start of the flyscan"""
|
|
@@ -38,16 +35,14 @@ class StandardFlyer(
|
|
|
38
35
|
):
|
|
39
36
|
def __init__(
|
|
40
37
|
self,
|
|
41
|
-
trigger_logic:
|
|
42
|
-
configuration_signals: Sequence[SignalR] = (),
|
|
38
|
+
trigger_logic: FlyerController[T],
|
|
43
39
|
name: str = "",
|
|
44
40
|
):
|
|
45
41
|
self._trigger_logic = trigger_logic
|
|
46
|
-
self._configuration_signals = tuple(configuration_signals)
|
|
47
42
|
super().__init__(name=name)
|
|
48
43
|
|
|
49
44
|
@property
|
|
50
|
-
def trigger_logic(self) ->
|
|
45
|
+
def trigger_logic(self) -> FlyerController[T]:
|
|
51
46
|
return self._trigger_logic
|
|
52
47
|
|
|
53
48
|
@AsyncStatus.wrap
|
|
@@ -73,13 +68,3 @@ class StandardFlyer(
|
|
|
73
68
|
@AsyncStatus.wrap
|
|
74
69
|
async def complete(self) -> None:
|
|
75
70
|
await self._trigger_logic.complete()
|
|
76
|
-
|
|
77
|
-
async def describe_configuration(self) -> dict[str, DataKey]:
|
|
78
|
-
return await merge_gathered_dicts(
|
|
79
|
-
[sig.describe() for sig in self._configuration_signals]
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
async def read_configuration(self) -> dict[str, Reading]:
|
|
83
|
-
return await merge_gathered_dicts(
|
|
84
|
-
[sig.read() for sig in self._configuration_signals]
|
|
85
|
-
)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import TypeVar, get_args, get_origin
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, model_validator
|
|
6
|
+
|
|
7
|
+
TableSubclass = TypeVar("TableSubclass", bound="Table")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _concat(value1, value2):
|
|
11
|
+
if isinstance(value1, np.ndarray):
|
|
12
|
+
return np.concatenate((value1, value2))
|
|
13
|
+
else:
|
|
14
|
+
return value1 + value2
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Table(BaseModel):
|
|
18
|
+
"""An abstraction of a Table of str to numpy array."""
|
|
19
|
+
|
|
20
|
+
model_config = ConfigDict(validate_assignment=True, strict=False)
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def row(cls: type[TableSubclass], **kwargs) -> TableSubclass: # type: ignore
|
|
24
|
+
arrayified_kwargs = {}
|
|
25
|
+
for field_name, field_value in cls.model_fields.items():
|
|
26
|
+
value = kwargs.pop(field_name)
|
|
27
|
+
if field_value.default_factory is None:
|
|
28
|
+
raise ValueError(
|
|
29
|
+
"`Table` models should have default factories for their "
|
|
30
|
+
"mutable empty columns."
|
|
31
|
+
)
|
|
32
|
+
default_array = field_value.default_factory()
|
|
33
|
+
if isinstance(default_array, np.ndarray):
|
|
34
|
+
arrayified_kwargs[field_name] = np.array(
|
|
35
|
+
[value], dtype=default_array.dtype
|
|
36
|
+
)
|
|
37
|
+
elif issubclass(type(value), Enum) and isinstance(value, str):
|
|
38
|
+
arrayified_kwargs[field_name] = [value]
|
|
39
|
+
else:
|
|
40
|
+
raise TypeError(
|
|
41
|
+
"Row column should be numpy arrays or sequence of string `Enum`."
|
|
42
|
+
)
|
|
43
|
+
if kwargs:
|
|
44
|
+
raise TypeError(
|
|
45
|
+
f"Unexpected keyword arguments {kwargs.keys()} for {cls.__name__}."
|
|
46
|
+
)
|
|
47
|
+
return cls(**arrayified_kwargs)
|
|
48
|
+
|
|
49
|
+
def __add__(self, right: TableSubclass) -> TableSubclass:
|
|
50
|
+
"""Concatenate the arrays in field values."""
|
|
51
|
+
|
|
52
|
+
if type(right) is not type(self):
|
|
53
|
+
raise RuntimeError(
|
|
54
|
+
f"{right} is not a `Table`, or is not the same "
|
|
55
|
+
f"type of `Table` as {self}."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return type(right)(
|
|
59
|
+
**{
|
|
60
|
+
field_name: _concat(
|
|
61
|
+
getattr(self, field_name), getattr(right, field_name)
|
|
62
|
+
)
|
|
63
|
+
for field_name in self.model_fields
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def numpy_dtype(self) -> np.dtype:
|
|
68
|
+
dtype = []
|
|
69
|
+
for field_name, field_value in self.model_fields.items():
|
|
70
|
+
if np.ndarray in (
|
|
71
|
+
get_origin(field_value.annotation),
|
|
72
|
+
field_value.annotation,
|
|
73
|
+
):
|
|
74
|
+
dtype.append((field_name, getattr(self, field_name).dtype))
|
|
75
|
+
else:
|
|
76
|
+
enum_type = get_args(field_value.annotation)[0]
|
|
77
|
+
assert issubclass(enum_type, Enum)
|
|
78
|
+
enum_values = [element.value for element in enum_type]
|
|
79
|
+
max_length_in_enum = max(len(value) for value in enum_values)
|
|
80
|
+
dtype.append((field_name, np.dtype(f"<U{max_length_in_enum}")))
|
|
81
|
+
|
|
82
|
+
return np.dtype(dtype)
|
|
83
|
+
|
|
84
|
+
def numpy_table(self):
|
|
85
|
+
# It would be nice to be able to use np.transpose for this,
|
|
86
|
+
# but it defaults to the largest dtype for everything.
|
|
87
|
+
dtype = self.numpy_dtype()
|
|
88
|
+
transposed_list = [
|
|
89
|
+
np.array(tuple(row), dtype=dtype)
|
|
90
|
+
for row in zip(*self.numpy_columns(), strict=False)
|
|
91
|
+
]
|
|
92
|
+
transposed = np.array(transposed_list, dtype=dtype)
|
|
93
|
+
return transposed
|
|
94
|
+
|
|
95
|
+
def numpy_columns(self) -> list[np.ndarray]:
|
|
96
|
+
"""Columns in the table can be lists of string enums or numpy arrays.
|
|
97
|
+
|
|
98
|
+
This method returns the columns, converting the string enums to numpy arrays.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
columns = []
|
|
102
|
+
for field_name, field_value in self.model_fields.items():
|
|
103
|
+
if np.ndarray in (
|
|
104
|
+
get_origin(field_value.annotation),
|
|
105
|
+
field_value.annotation,
|
|
106
|
+
):
|
|
107
|
+
columns.append(getattr(self, field_name))
|
|
108
|
+
else:
|
|
109
|
+
enum_type = get_args(field_value.annotation)[0]
|
|
110
|
+
assert issubclass(enum_type, Enum)
|
|
111
|
+
enum_values = [element.value for element in enum_type]
|
|
112
|
+
max_length_in_enum = max(len(value) for value in enum_values)
|
|
113
|
+
dtype = np.dtype(f"<U{max_length_in_enum}")
|
|
114
|
+
|
|
115
|
+
columns.append(
|
|
116
|
+
np.array(
|
|
117
|
+
[enum.value for enum in getattr(self, field_name)], dtype=dtype
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return columns
|
|
122
|
+
|
|
123
|
+
@model_validator(mode="after")
|
|
124
|
+
def validate_arrays(self) -> "Table":
|
|
125
|
+
first_length = len(next(iter(self))[1])
|
|
126
|
+
assert all(
|
|
127
|
+
len(field_value) == first_length for _, field_value in self
|
|
128
|
+
), "Rows should all be of equal size."
|
|
129
|
+
|
|
130
|
+
if not all(
|
|
131
|
+
# Checks if the values are numpy subtypes if the array is a numpy array,
|
|
132
|
+
# or if the value is a string enum.
|
|
133
|
+
np.issubdtype(getattr(self, field_name).dtype, default_array.dtype)
|
|
134
|
+
if isinstance(
|
|
135
|
+
default_array := self.model_fields[field_name].default_factory(), # type: ignore
|
|
136
|
+
np.ndarray,
|
|
137
|
+
)
|
|
138
|
+
else issubclass(get_args(field_value.annotation)[0], Enum)
|
|
139
|
+
for field_name, field_value in self.model_fields.items()
|
|
140
|
+
):
|
|
141
|
+
raise ValueError(
|
|
142
|
+
f"Cannot construct a `{type(self).__name__}`, "
|
|
143
|
+
"some rows have incorrect types."
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
return self
|
{ophyd_async-0.6.0 → ophyd_async-0.7.0}/src/ophyd_async/epics/adaravis/_aravis_controller.py
RENAMED
|
@@ -2,7 +2,7 @@ import asyncio
|
|
|
2
2
|
from typing import Literal
|
|
3
3
|
|
|
4
4
|
from ophyd_async.core import (
|
|
5
|
-
|
|
5
|
+
DetectorController,
|
|
6
6
|
DetectorTrigger,
|
|
7
7
|
TriggerInfo,
|
|
8
8
|
set_and_wait_for_value,
|
|
@@ -18,7 +18,7 @@ from ._aravis_io import AravisDriverIO, AravisTriggerMode, AravisTriggerSource
|
|
|
18
18
|
_HIGHEST_POSSIBLE_DEADTIME = 1961e-6
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class AravisController(
|
|
21
|
+
class AravisController(DetectorController):
|
|
22
22
|
GPIO_NUMBER = Literal[1, 2, 3, 4]
|
|
23
23
|
|
|
24
24
|
def __init__(self, driver: AravisDriverIO, gpio_number: GPIO_NUMBER) -> None:
|
|
@@ -30,7 +30,7 @@ class AravisController(DetectorControl):
|
|
|
30
30
|
return _HIGHEST_POSSIBLE_DEADTIME
|
|
31
31
|
|
|
32
32
|
async def prepare(self, trigger_info: TriggerInfo):
|
|
33
|
-
if
|
|
33
|
+
if trigger_info.total_number_of_triggers == 0:
|
|
34
34
|
image_mode = adcore.ImageMode.continuous
|
|
35
35
|
else:
|
|
36
36
|
image_mode = adcore.ImageMode.multiple
|
|
@@ -43,7 +43,7 @@ class AravisController(DetectorControl):
|
|
|
43
43
|
|
|
44
44
|
await asyncio.gather(
|
|
45
45
|
self._drv.trigger_source.set(trigger_source),
|
|
46
|
-
self._drv.num_images.set(
|
|
46
|
+
self._drv.num_images.set(trigger_info.total_number_of_triggers),
|
|
47
47
|
self._drv.image_mode.set(image_mode),
|
|
48
48
|
)
|
|
49
49
|
|