ophyd-async 0.8.0a2__tar.gz → 0.8.0a4__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.8.0a2 → ophyd_async-0.8.0a4}/.copier-answers.yml +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/CONTRIBUTING.md +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/workflows/_pypi.yml +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/workflows/_release.yml +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/workflows/ci.yml +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/PKG-INFO +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/examples/foo_detector.py +1 -1
- ophyd_async-0.8.0a4/docs/explanations/decisions/0009-procedural-vs-declarative-devices.md +140 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/pyproject.toml +2 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/_version.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/__init__.py +9 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_device.py +71 -49
- ophyd_async-0.8.0a4/src/ophyd_async/core/_device_filler.py +269 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_mock_signal_backend.py +10 -7
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_mock_signal_utils.py +14 -11
- ophyd_async-0.8.0a4/src/ophyd_async/core/_readable.py +260 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_signal.py +22 -24
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_soft_signal_backend.py +2 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_utils.py +64 -11
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adaravis/_aravis_io.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adcore/_core_io.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adcore/_single_trigger.py +6 -10
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adkinetix/_kinetix_io.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adpilatus/_pilatus_io.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/advimba/_vimba_io.py +1 -1
- ophyd_async-0.8.0a4/src/ophyd_async/epics/core/__init__.py +26 -0
- {ophyd_async-0.8.0a2/src/ophyd_async/epics/signal → ophyd_async-0.8.0a4/src/ophyd_async/epics/core}/_aioca.py +3 -6
- ophyd_async-0.8.0a4/src/ophyd_async/epics/core/_epics_connector.py +53 -0
- ophyd_async-0.8.0a4/src/ophyd_async/epics/core/_epics_device.py +13 -0
- {ophyd_async-0.8.0a2/src/ophyd_async/epics/signal → ophyd_async-0.8.0a4/src/ophyd_async/epics/core}/_p4p.py +3 -6
- ophyd_async-0.8.0a4/src/ophyd_async/epics/core/_pvi_connector.py +91 -0
- {ophyd_async-0.8.0a2/src/ophyd_async/epics/signal → ophyd_async-0.8.0a4/src/ophyd_async/epics/core}/_signal.py +31 -16
- ophyd_async-0.8.0a2/src/ophyd_async/epics/signal/_common.py → ophyd_async-0.8.0a4/src/ophyd_async/epics/core/_util.py +19 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/demo/_mover.py +4 -5
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/demo/_sensor.py +9 -12
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/eiger/_eiger_io.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/eiger/_odin_io.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/motor.py +4 -5
- ophyd_async-0.8.0a4/src/ophyd_async/epics/signal.py +11 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/fastcs/core.py +2 -2
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/plan_stubs/_ensure_connected.py +2 -4
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/sim/demo/_sim_motor.py +3 -4
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/base_devices/_base_device.py +48 -48
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/demo/_counter.py +6 -16
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/demo/_mover.py +3 -4
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async.egg-info/PKG-INFO +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async.egg-info/SOURCES.txt +10 -7
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/system_tests/epics/eiger/test_eiger_system.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_device.py +22 -14
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_device_save_loader.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_flyer.py +5 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_mock_signal_backend.py +3 -3
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_readable.py +81 -58
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_signal.py +23 -64
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_soft_signal_backend.py +8 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_subset_enum.py +3 -3
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_utils.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/adaravis/test_aravis.py +3 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/adcore/test_writers.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/adkinetix/test_kinetix.py +3 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/adsimdetector/test_sim.py +7 -3
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/advimba/test_vimba.py +3 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/demo/test_demo.py +10 -5
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/eiger/test_odin_io.py +3 -4
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/pvi/test_pvi.py +33 -4
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/signal/test_common.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/signal/test_signals.py +15 -4
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/test_motor.py +18 -10
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/fastcs/panda/db/panda.db +8 -8
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/fastcs/panda/test_hdf_panda.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/fastcs/panda/test_panda_connect.py +6 -2
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/fastcs/panda/test_panda_control.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/fastcs/panda/test_panda_utils.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/fastcs/panda/test_writer.py +3 -2
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/plan_stubs/test_ensure_connected.py +3 -3
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/plan_stubs/test_fly.py +1 -1
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/sim/demo/test_sim_motor.py +2 -2
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/sim/test_sim_detector.py +3 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/tango/test_base_device.py +8 -18
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/tango/test_tango_transport.py +5 -3
- ophyd_async-0.8.0a2/src/ophyd_async/core/_device_filler.py +0 -191
- ophyd_async-0.8.0a2/src/ophyd_async/core/_readable.py +0 -261
- ophyd_async-0.8.0a2/src/ophyd_async/epics/pvi/__init__.py +0 -3
- ophyd_async-0.8.0a2/src/ophyd_async/epics/pvi/_pvi.py +0 -73
- ophyd_async-0.8.0a2/src/ophyd_async/epics/signal/__init__.py +0 -20
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.codecov.yml +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.devcontainer/devcontainer.json +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.git-blame-ignore-revs +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/ISSUE_TEMPLATE/issue.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/actions/install_requirements/action.yml +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/dependabot.yml +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/pages/index.html +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/pages/make_switcher.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/workflows/_check.yml +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/workflows/_dist.yml +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/workflows/_docs.yml +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/workflows/_test.yml +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/workflows/_tox.yml +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.github/workflows/periodic.yml +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.gitignore +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.mailmap +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/.pre-commit-config.yaml +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/Dockerfile +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/LICENSE +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/README.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/_api.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/_templates/custom-module-template.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/conf.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/examples/epics_demo.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/examples/tango_demo.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/decisions/0001-record-architecture-decisions.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/decisions/0002-switched-to-python-copier-template.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/decisions/0003-ophyd-async-migration.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/decisions/0004-repository-structure.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/decisions/0005-respect-black-line-length.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/decisions/0006-procedural-device-definitions.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/decisions/0007-subpackage-structure.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/decisions/0008-signal-types.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/decisions/COPYME +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/decisions.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/design-goals.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/event-loop-choice.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations/flyscanning.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/explanations.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/genindex.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/how-to/choose-interfaces-for-devices.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/how-to/compound-devices.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/how-to/contribute.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/how-to/make-a-simple-device.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/how-to/make-a-standard-detector.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/how-to/write-tests-for-devices.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/how-to.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/images/ophyd-async-logo.svg +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/images/ophyd-favicon.svg +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/index.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/reference.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/tutorials/installation.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/tutorials/using-existing-devices.rst +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/docs/tutorials.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/setup.cfg +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/__main__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_detector.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_device_save_loader.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_flyer.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_hdf_dataset.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_log.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_protocol.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_providers.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_signal_backend.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_status.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/core/_table.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adaravis/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adaravis/_aravis.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adaravis/_aravis_controller.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adcore/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adcore/_core_logic.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adcore/_hdf_writer.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adcore/_utils.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adkinetix/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adkinetix/_kinetix.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adkinetix/_kinetix_controller.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adpilatus/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adpilatus/_pilatus.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adpilatus/_pilatus_controller.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adsimdetector/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adsimdetector/_sim.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/adsimdetector/_sim_controller.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/advimba/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/advimba/_vimba.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/advimba/_vimba_controller.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/demo/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/demo/mover.db +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/demo/sensor.db +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/eiger/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/eiger/_eiger.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/epics/eiger/_eiger_controller.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/fastcs/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/fastcs/odin/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/fastcs/panda/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/fastcs/panda/_block.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/fastcs/panda/_control.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/fastcs/panda/_hdf_panda.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/fastcs/panda/_table.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/fastcs/panda/_trigger.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/fastcs/panda/_utils.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/fastcs/panda/_writer.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/plan_stubs/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/plan_stubs/_fly.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/plan_stubs/_nd_attributes.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/py.typed +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/sim/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/sim/demo/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/sim/demo/_pattern_detector/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/sim/demo/_pattern_detector/_pattern_detector.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/sim/demo/_pattern_detector/_pattern_detector_controller.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/sim/demo/_pattern_detector/_pattern_detector_writer.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/sim/demo/_pattern_detector/_pattern_generator.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/sim/testing/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/base_devices/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/base_devices/_tango_readable.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/demo/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/demo/_detector.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/demo/_tango/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/demo/_tango/_servers.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/signal/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/signal/_signal.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async/tango/signal/_tango_transport.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async.egg-info/dependency_links.txt +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async.egg-info/entry_points.txt +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async.egg-info/requires.txt +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/src/ophyd_async.egg-info/top_level.txt +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/system_tests/epics/eiger/README.md +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/system_tests/epics/eiger/start_iocs_and_run_tests.sh +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/conftest.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_device_collector.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_log.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_protocol.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_providers.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_status.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_table.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/core/test_watchable_async_status.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/adcore/test_drivers.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/adcore/test_scans.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/adcore/test_single_trigger.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/adpilatus/test_pilatus.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/conftest.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/eiger/test_eiger_controller.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/eiger/test_eiger_detector.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/signal/test_records.db +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/epics/test_areadetector_subclass_naming.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/fastcs/panda/test_seq_table.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/fastcs/panda/test_trigger.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/sim/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/sim/conftest.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/sim/demo/__init__.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/sim/test_pattern_generator.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/sim/test_sim_writer.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/sim/test_streaming_plan.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/tango/test_tango_signals.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/test_cli.py +0 -0
- {ophyd_async-0.8.0a2 → ophyd_async-0.8.0a4}/tests/test_data/test_yaml_save.yml +0 -0
|
@@ -24,4 +24,4 @@ It is recommended that developers use a [vscode devcontainer](https://code.visua
|
|
|
24
24
|
|
|
25
25
|
This project was created using the [Diamond Light Source Copier Template](https://github.com/DiamondLightSource/python-copier-template) for Python projects.
|
|
26
26
|
|
|
27
|
-
For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/2.
|
|
27
|
+
For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/2.5.0/how-to.html).
|
|
@@ -23,7 +23,7 @@ jobs:
|
|
|
23
23
|
- name: Create GitHub Release
|
|
24
24
|
# We pin to the SHA, not the tag, for security reasons.
|
|
25
25
|
# https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions
|
|
26
|
-
uses: softprops/action-gh-release@
|
|
26
|
+
uses: softprops/action-gh-release@e7a8f85e1c67a31e6ed99a94b41bd0b71bbee6b8 # v2.0.9
|
|
27
27
|
with:
|
|
28
28
|
prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }}
|
|
29
29
|
files: "*"
|
|
@@ -20,7 +20,7 @@ jobs:
|
|
|
20
20
|
if: needs.check.outputs.branch-pr == ''
|
|
21
21
|
strategy:
|
|
22
22
|
matrix:
|
|
23
|
-
runs-on: ["ubuntu-latest"] # can add
|
|
23
|
+
runs-on: ["ubuntu-latest", "windows-latest"] # can add macos-latest
|
|
24
24
|
python-version: ["3.10","3.11"] # 3.12 should be added when p4p is updated
|
|
25
25
|
include:
|
|
26
26
|
# Include one that runs in the dev environment
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ophyd-async
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.0a4
|
|
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
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# 9. Procedural vs Declarative Devices
|
|
2
|
+
|
|
3
|
+
Date: 01/10/24
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
In [](./0006-procedural-device-definitions.rst) we decided we preferred the procedural approach to devices, because of the issue of applying structure like `DeviceVector`. Since then we have `FastCS` and `Tango` support which use a declarative approach. We need to decide whether we are happy with this situation, or whether we should go all in one way or the other. A suitable test Device would be:
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
class EpicsProceduralDevice(StandardReadable):
|
|
15
|
+
def __init__(self, prefix: str, num_values: int, name="") -> None:
|
|
16
|
+
with self.add_children_as_readables():
|
|
17
|
+
self.value = DeviceVector(
|
|
18
|
+
{
|
|
19
|
+
i: epics_signal_r(float, f"{prefix}Value{i}")
|
|
20
|
+
for i in range(1, num_values + 1)
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
with self.add_children_as_readables(ConfigSignal):
|
|
24
|
+
self.mode = epics_signal_rw(EnergyMode, prefix + "Mode")
|
|
25
|
+
super().__init__(name=name)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
and a Tango/FastCS procedural equivalent would be (if we add support to StandardReadable for Format.HINTED_SIGNAL and Format.CONFIG_SIGNAL annotations):
|
|
29
|
+
```python
|
|
30
|
+
class TangoDeclarativeDevice(StandardReadable, TangoDevice):
|
|
31
|
+
value: Annotated[DeviceVector[SignalR[float]], Format.HINTED_SIGNAL]
|
|
32
|
+
mode: Annotated[SignalRW[EnergyMode], Format.CONFIG_SIGNAL]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
But we could specify the Tango one procedurally (with some slight ugliness around the DeviceVector):
|
|
36
|
+
```python
|
|
37
|
+
class TangoProceduralDevice(StandardReadable):
|
|
38
|
+
def __init__(self, prefix: str, name="") -> None:
|
|
39
|
+
with self.add_children_as_readables():
|
|
40
|
+
self.value = DeviceVector({0: tango_signal_r(float)})
|
|
41
|
+
with self.add_children_as_readables(ConfigSignal):
|
|
42
|
+
self.mode = tango_signal_rw(EnergyMode)
|
|
43
|
+
super().__init__(name=name, connector=TangoConnector(prefix))
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
or the EPICS one could be declarative:
|
|
47
|
+
```python
|
|
48
|
+
class EpicsDeclarativeDevice(StandardReadable, EpicsDevice):
|
|
49
|
+
value: Annotated[
|
|
50
|
+
DeviceVector[SignalR[float]], Format.HINTED_SIGNAL, EpicsSuffix("Value%d", "num_values")
|
|
51
|
+
]
|
|
52
|
+
mode: Annotated[SignalRW[EnergyMode], Format.CONFIG_SIGNAL, EpicsSuffix("Mode")]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Which do we prefer?
|
|
56
|
+
|
|
57
|
+
## Decision
|
|
58
|
+
|
|
59
|
+
We decided that the declarative approach is to be preferred until we need to write formatted strings. At that point we should drop to an `__init__` method and a for loop. This is not a step towards only supporting the declarative approach and there are no plans to drop the procedural approach.
|
|
60
|
+
|
|
61
|
+
The two approaches now look like:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
class Sensor(StandardReadable, EpicsDevice):
|
|
65
|
+
"""A demo sensor that produces a scalar value based on X and Y Movers"""
|
|
66
|
+
|
|
67
|
+
value: A[SignalR[float], PvSuffix("Value"), Format.HINTED_SIGNAL]
|
|
68
|
+
mode: A[SignalRW[EnergyMode], PvSuffix("Mode"), Format.CONFIG_SIGNAL]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class SensorGroup(StandardReadable):
|
|
72
|
+
def __init__(self, prefix: str, name: str = "", sensor_count: int = 3) -> None:
|
|
73
|
+
with self.add_children_as_readables():
|
|
74
|
+
self.sensors = DeviceVector(
|
|
75
|
+
{i: Sensor(f"{prefix}{i}:") for i in range(1, sensor_count + 1)}
|
|
76
|
+
)
|
|
77
|
+
super().__init__(name)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Consequences
|
|
81
|
+
|
|
82
|
+
We need to:
|
|
83
|
+
- Add support for reading annotations and `PvSuffix` in an `ophyd_async.epics.core.EpicsDevice` baseclass
|
|
84
|
+
- Do the `Format.HINTED_SIGNAL` and `Format.CONFIG_SIGNAL` flags in annotations for `StandardReadable`
|
|
85
|
+
- Ensure we can always drop to `__init__`
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
## pvi structure changes
|
|
89
|
+
Structure read from `.value` now includes `DeviceVector` support. Requires at least PandABlocks-ioc 0.11.2
|
|
90
|
+
|
|
91
|
+
## Epics `signal` module moves
|
|
92
|
+
`ophyd_async.epics.signal` moves to `ophyd_async.epics.core` with a backwards compat module that emits deprecation warning.
|
|
93
|
+
```python
|
|
94
|
+
# old
|
|
95
|
+
from ophyd_async.epics.signal import epics_signal_rw
|
|
96
|
+
# new
|
|
97
|
+
from ophyd_async.epics.core import epics_signal_rw
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## `StandardReadable` wrappers change to `StandardReadableFormat`
|
|
101
|
+
`StandardReadable` wrappers change to enum members of `StandardReadableFormat` (normally imported as `Format`)
|
|
102
|
+
```python
|
|
103
|
+
# old
|
|
104
|
+
from ophyd_async.core import ConfigSignal, HintedSignal
|
|
105
|
+
class MyDevice(StandardReadable):
|
|
106
|
+
def __init__(self):
|
|
107
|
+
self.add_readables([sig1], ConfigSignal)
|
|
108
|
+
self.add_readables([sig2], HintedSignal)
|
|
109
|
+
self.add_readables([sig3], HintedSignal.uncached)
|
|
110
|
+
# new
|
|
111
|
+
from ophyd_async.core import StandardReadableFormat as Format
|
|
112
|
+
class MyDevice(StandardReadable):
|
|
113
|
+
def __init__(self):
|
|
114
|
+
self.add_readables([sig1], Format.CONFIG_SIGNAL)
|
|
115
|
+
self.add_readables([sig2], Format.HINTED_SIGNAL)
|
|
116
|
+
self.add_readables([sig3], Format.HINTED_UNCACHED_SIGNAL
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Declarative Devices are now available
|
|
120
|
+
```python
|
|
121
|
+
# old
|
|
122
|
+
from ophyd_async.core import ConfigSignal, HintedSignal
|
|
123
|
+
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
|
|
124
|
+
|
|
125
|
+
class Sensor(StandardReadable):
|
|
126
|
+
def __init__(self, prefix: str, name="") -> None:
|
|
127
|
+
with self.add_children_as_readables(HintedSignal):
|
|
128
|
+
self.value = epics_signal_r(float, prefix + "Value")
|
|
129
|
+
with self.add_children_as_readables(ConfigSignal):
|
|
130
|
+
self.mode = epics_signal_rw(EnergyMode, prefix + "Mode")
|
|
131
|
+
super().__init__(name=name)
|
|
132
|
+
# new
|
|
133
|
+
from typing import Annotated as A
|
|
134
|
+
from ophyd_async.core import StandardReadableFormat as Format
|
|
135
|
+
from ophyd_async.epics.core import EpicsDevice, PvSuffix, epics_signal_r, epics_signal_rw
|
|
136
|
+
|
|
137
|
+
class Sensor(StandardReadable, EpicsDevice):
|
|
138
|
+
value: A[SignalR[float], PvSuffix("Value"), Format.HINTED_SIGNAL]
|
|
139
|
+
mode: A[SignalRW[EnergyMode], PvSuffix("Mode"), Format.CONFIG_SIGNAL]
|
|
140
|
+
```
|
|
@@ -94,7 +94,8 @@ reportMissingImports = false # Ignore missing stubs in imported modules
|
|
|
94
94
|
# Run pytest with all our checkers, and don't spam us with massive tracebacks on error
|
|
95
95
|
addopts = """
|
|
96
96
|
--tb=native -vv --strict-markers --doctest-modules
|
|
97
|
-
--doctest-glob="*.rst" --doctest-glob="*.md"
|
|
97
|
+
--doctest-glob="*.rst" --doctest-glob="*.md"
|
|
98
|
+
--ignore=docs/examples --ignore=src/ophyd_async/epics/signal.py
|
|
98
99
|
"""
|
|
99
100
|
# https://iscinumpy.gitlab.io/post/bound-version-constraints/#watch-for-warnings
|
|
100
101
|
filterwarnings = "error"
|
|
@@ -45,7 +45,12 @@ from ._providers import (
|
|
|
45
45
|
UUIDFilenameProvider,
|
|
46
46
|
YMDPathProvider,
|
|
47
47
|
)
|
|
48
|
-
from ._readable import
|
|
48
|
+
from ._readable import (
|
|
49
|
+
ConfigSignal,
|
|
50
|
+
HintedSignal,
|
|
51
|
+
StandardReadable,
|
|
52
|
+
StandardReadableFormat,
|
|
53
|
+
)
|
|
49
54
|
from ._signal import (
|
|
50
55
|
Signal,
|
|
51
56
|
SignalR,
|
|
@@ -78,6 +83,7 @@ from ._utils import (
|
|
|
78
83
|
DEFAULT_TIMEOUT,
|
|
79
84
|
CalculatableTimeout,
|
|
80
85
|
Callback,
|
|
86
|
+
LazyMock,
|
|
81
87
|
NotConnected,
|
|
82
88
|
Reference,
|
|
83
89
|
StrictEnum,
|
|
@@ -141,6 +147,7 @@ __all__ = [
|
|
|
141
147
|
"ConfigSignal",
|
|
142
148
|
"HintedSignal",
|
|
143
149
|
"StandardReadable",
|
|
150
|
+
"StandardReadableFormat",
|
|
144
151
|
"Signal",
|
|
145
152
|
"SignalR",
|
|
146
153
|
"SignalRW",
|
|
@@ -170,6 +177,7 @@ __all__ = [
|
|
|
170
177
|
"DEFAULT_TIMEOUT",
|
|
171
178
|
"CalculatableTimeout",
|
|
172
179
|
"Callback",
|
|
180
|
+
"LazyMock",
|
|
173
181
|
"CALCULATE_TIMEOUT",
|
|
174
182
|
"NotConnected",
|
|
175
183
|
"Reference",
|
|
@@ -3,17 +3,15 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import sys
|
|
5
5
|
from collections.abc import Coroutine, Iterator, Mapping, MutableMapping
|
|
6
|
+
from functools import cached_property
|
|
6
7
|
from logging import LoggerAdapter, getLogger
|
|
7
8
|
from typing import Any, TypeVar
|
|
8
|
-
from unittest.mock import Mock
|
|
9
9
|
|
|
10
10
|
from bluesky.protocols import HasName
|
|
11
11
|
from bluesky.run_engine import call_in_bluesky_event_loop, in_bluesky_event_loop
|
|
12
12
|
|
|
13
13
|
from ._protocol import Connectable
|
|
14
|
-
from ._utils import DEFAULT_TIMEOUT, NotConnected, wait_for_connection
|
|
15
|
-
|
|
16
|
-
_device_mocks: dict[Device, Mock] = {}
|
|
14
|
+
from ._utils import DEFAULT_TIMEOUT, LazyMock, NotConnected, wait_for_connection
|
|
17
15
|
|
|
18
16
|
|
|
19
17
|
class DeviceConnector:
|
|
@@ -37,25 +35,23 @@ class DeviceConnector:
|
|
|
37
35
|
during ``__init__``.
|
|
38
36
|
"""
|
|
39
37
|
|
|
40
|
-
async def
|
|
41
|
-
|
|
42
|
-
device:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
):
|
|
38
|
+
async def connect_mock(self, device: Device, mock: LazyMock):
|
|
39
|
+
# Connect serially, no errors to gather up as in mock mode
|
|
40
|
+
for name, child_device in device.children():
|
|
41
|
+
await child_device.connect(mock=mock.child(name))
|
|
42
|
+
|
|
43
|
+
async def connect_real(self, device: Device, timeout: float, force_reconnect: bool):
|
|
47
44
|
"""Used during ``Device.connect``.
|
|
48
45
|
|
|
49
46
|
This is called when a previous connect has not been done, or has been
|
|
50
47
|
done in a different mock more. It should connect the Device and all its
|
|
51
48
|
children.
|
|
52
49
|
"""
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
)
|
|
50
|
+
# Connect in parallel, gathering up NotConnected errors
|
|
51
|
+
coros = {
|
|
52
|
+
name: child_device.connect(timeout=timeout, force_reconnect=force_reconnect)
|
|
53
|
+
for name, child_device in device.children()
|
|
54
|
+
}
|
|
59
55
|
await wait_for_connection(**coros)
|
|
60
56
|
|
|
61
57
|
|
|
@@ -67,14 +63,14 @@ class Device(HasName, Connectable):
|
|
|
67
63
|
parent: Device | None = None
|
|
68
64
|
# None if connect hasn't started, a Task if it has
|
|
69
65
|
_connect_task: asyncio.Task | None = None
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
_connect_mock_arg: bool | None = None
|
|
66
|
+
# The mock if we have connected in mock mode
|
|
67
|
+
_mock: LazyMock | None = None
|
|
73
68
|
|
|
74
69
|
def __init__(
|
|
75
70
|
self, name: str = "", connector: DeviceConnector | None = None
|
|
76
71
|
) -> None:
|
|
77
72
|
self._connector = connector or DeviceConnector()
|
|
73
|
+
self._connector.create_children_from_annotations(self)
|
|
78
74
|
self.set_name(name)
|
|
79
75
|
|
|
80
76
|
@property
|
|
@@ -82,10 +78,18 @@ class Device(HasName, Connectable):
|
|
|
82
78
|
"""Return the name of the Device"""
|
|
83
79
|
return self._name
|
|
84
80
|
|
|
81
|
+
@cached_property
|
|
82
|
+
def _child_devices(self) -> dict[str, Device]:
|
|
83
|
+
return {}
|
|
84
|
+
|
|
85
85
|
def children(self) -> Iterator[tuple[str, Device]]:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
yield from self._child_devices.items()
|
|
87
|
+
|
|
88
|
+
@cached_property
|
|
89
|
+
def log(self) -> LoggerAdapter:
|
|
90
|
+
return LoggerAdapter(
|
|
91
|
+
getLogger("ophyd_async.devices"), {"ophyd_async_device_name": self.name}
|
|
92
|
+
)
|
|
89
93
|
|
|
90
94
|
def set_name(self, name: str):
|
|
91
95
|
"""Set ``self.name=name`` and each ``self.child.name=name+"-child"``.
|
|
@@ -96,28 +100,33 @@ class Device(HasName, Connectable):
|
|
|
96
100
|
New name to set
|
|
97
101
|
"""
|
|
98
102
|
self._name = name
|
|
99
|
-
# Ensure
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
)
|
|
103
|
+
# Ensure logger is recreated after a name change
|
|
104
|
+
if "log" in self.__dict__:
|
|
105
|
+
del self.log
|
|
103
106
|
for child_name, child in self.children():
|
|
104
107
|
child_name = f"{self.name}-{child_name.strip('_')}" if self.name else ""
|
|
105
108
|
child.set_name(child_name)
|
|
106
109
|
|
|
107
110
|
def __setattr__(self, name: str, value: Any) -> None:
|
|
111
|
+
# Bear in mind that this function is called *a lot*, so
|
|
112
|
+
# we need to make sure nothing expensive happens in it...
|
|
108
113
|
if name == "parent":
|
|
109
114
|
if self.parent not in (value, None):
|
|
110
115
|
raise TypeError(
|
|
111
116
|
f"Cannot set the parent of {self} to be {value}: "
|
|
112
117
|
f"it is already a child of {self.parent}"
|
|
113
118
|
)
|
|
114
|
-
|
|
119
|
+
# ...hence not doing an isinstance check for attributes we
|
|
120
|
+
# know not to be Devices
|
|
121
|
+
elif name not in _not_device_attrs and isinstance(value, Device):
|
|
115
122
|
value.parent = self
|
|
116
|
-
|
|
123
|
+
self._child_devices[name] = value
|
|
124
|
+
# ...and avoiding the super call as we know it resolves to `object`
|
|
125
|
+
return object.__setattr__(self, name, value)
|
|
117
126
|
|
|
118
127
|
async def connect(
|
|
119
128
|
self,
|
|
120
|
-
mock: bool |
|
|
129
|
+
mock: bool | LazyMock = False,
|
|
121
130
|
timeout: float = DEFAULT_TIMEOUT,
|
|
122
131
|
force_reconnect: bool = False,
|
|
123
132
|
) -> None:
|
|
@@ -132,26 +141,39 @@ class Device(HasName, Connectable):
|
|
|
132
141
|
timeout:
|
|
133
142
|
Time to wait before failing with a TimeoutError.
|
|
134
143
|
"""
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
144
|
+
if mock:
|
|
145
|
+
# Always connect in mock mode serially
|
|
146
|
+
if isinstance(mock, LazyMock):
|
|
147
|
+
# Use the provided mock
|
|
148
|
+
self._mock = mock
|
|
149
|
+
elif not self._mock:
|
|
150
|
+
# Make one
|
|
151
|
+
self._mock = LazyMock()
|
|
152
|
+
await self._connector.connect_mock(self, self._mock)
|
|
153
|
+
else:
|
|
154
|
+
# Try to cache the connect in real mode
|
|
155
|
+
can_use_previous_connect = (
|
|
156
|
+
self._mock is None
|
|
157
|
+
and self._connect_task
|
|
158
|
+
and not (self._connect_task.done() and self._connect_task.exception())
|
|
149
159
|
)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
160
|
+
if force_reconnect or not can_use_previous_connect:
|
|
161
|
+
self._mock = None
|
|
162
|
+
coro = self._connector.connect_real(self, timeout, force_reconnect)
|
|
163
|
+
self._connect_task = asyncio.create_task(coro)
|
|
164
|
+
assert self._connect_task, "Connect task not created, this shouldn't happen"
|
|
165
|
+
# Wait for it to complete
|
|
166
|
+
await self._connect_task
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
_not_device_attrs = {
|
|
170
|
+
"_name",
|
|
171
|
+
"_children",
|
|
172
|
+
"_connector",
|
|
173
|
+
"_timeout",
|
|
174
|
+
"_mock",
|
|
175
|
+
"_connect_task",
|
|
176
|
+
}
|
|
155
177
|
|
|
156
178
|
|
|
157
179
|
DeviceT = TypeVar("DeviceT", bound=Device)
|