ophyd-async 0.3.1__tar.gz → 0.3.2__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.3.1 → ophyd_async-0.3.2}/PKG-INFO +2 -2
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/pyproject.toml +1 -1
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/_version.py +2 -2
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/signal.py +18 -6
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/soft_signal_backend.py +23 -9
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/_backend/_aioca.py +5 -3
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/_common_blocks.py +2 -1
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/_hdf_panda.py +0 -2
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/_table.py +10 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/writers/_hdf_writer.py +22 -105
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/writers/_panda_hdf_file.py +4 -8
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async.egg-info/PKG-INFO +2 -2
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async.egg-info/requires.txt +1 -1
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_mock_signal_backend.py +32 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_soft_signal_backend.py +37 -1
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/test_hdf_panda.py +22 -24
- ophyd_async-0.3.2/tests/panda/test_writer.py +198 -0
- ophyd_async-0.3.1/tests/panda/test_writer.py +0 -208
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.codecov.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.copier-answers.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.devcontainer/devcontainer.json +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.git-blame-ignore-revs +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/CONTRIBUTING.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/actions/install_requirements/action.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/dependabot.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/pages/index.html +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/pages/make_switcher.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_check.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_dist.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_docs.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_pypi.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_release.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_test.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_tox.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/ci.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/periodic.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.gitignore +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.mailmap +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.pre-commit-config.yaml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/Dockerfile +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/LICENSE +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/README.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/_templates/README +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/_templates/custom-class-template.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/_templates/custom-module-template.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/conf.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/examples/epics_demo.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/examples/foo_detector.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/0001-record-architecture-decisions.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/0002-switched-to-python-copier-template.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/0003-ophyd-async-migration.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/0004-repository-structure.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/0005-respect-black-line-length.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/0006-procedural-device-definitions.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/COPYME +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/design-goals.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/event-loop-choice.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/flyscanning.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/genindex.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to/choose-interfaces-for-devices.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to/compound-devices.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to/contribute.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to/make-a-simple-device.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to/make-a-standard-detector.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to/write-tests-for-devices.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/images/bluesky_ophyd_epics_devices_logo.svg +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/images/bluesky_ophyd_logo.svg +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/images/ophyd_favicon.svg +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/index.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/reference/api.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/reference.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/tutorials/installation.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/tutorials/using-existing-devices.rst +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/tutorials.md +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/setup.cfg +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/__main__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/_providers.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/async_status.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/detector.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/device.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/device_save_loader.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/flyer.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/mock_signal_backend.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/mock_signal_utils.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/signal_backend.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/standard_readable.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/utils.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/_backend/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/_backend/_p4p.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/_backend/common.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/aravis.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/controllers/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/controllers/ad_sim_controller.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/controllers/aravis_controller.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/controllers/kinetix_controller.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/controllers/pilatus_controller.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/controllers/vimba_controller.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/drivers/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/drivers/ad_base.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/drivers/aravis_driver.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/drivers/kinetix_driver.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/drivers/pilatus_driver.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/drivers/vimba_driver.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/kinetix.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/pilatus.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/single_trigger_det.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/utils.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/vimba.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/writers/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/writers/_hdfdataset.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/writers/_hdffile.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/writers/hdf_writer.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/writers/nd_file_hdf.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/writers/nd_plugin.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/demo/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/demo/demo_ad_sim_detector.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/demo/mover.db +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/demo/sensor.db +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/motion/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/motion/motor.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/pvi/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/pvi/pvi.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/signal/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/signal/_epics_transport.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/signal/signal.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/log.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/_panda_controller.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/_trigger.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/_utils.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/writers/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/plan_stubs/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/plan_stubs/ensure_connected.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/plan_stubs/fly.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/protocols.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/demo/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/demo/sim_motor.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/pattern_generator.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/sim_pattern_detector_control.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/sim_pattern_detector_writer.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/sim_pattern_generator.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async.egg-info/SOURCES.txt +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async.egg-info/dependency_links.txt +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async.egg-info/entry_points.txt +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async.egg-info/top_level.txt +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/conftest.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_async_status.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_device.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_device_collector.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_device_save_loader.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_flyer.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_signal.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_standard_readable.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_utils.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_watchable_async_status.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/_backend/test_common.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_aravis.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_controllers.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_drivers.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_kinetix.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_pilatus.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_scans.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_single_trigger_det.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_utils.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_vimba.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_writers.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/demo/test_demo.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/demo/test_demo_ad_sim_detector.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/motion/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/motion/test_motor.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/test_pvi.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/test_records.db +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/test_signals.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/db/panda.db +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/test_panda_connect.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/test_panda_controller.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/test_panda_utils.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/test_table.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/test_trigger.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/plan_stubs/test_fly.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/protocols/test_protocols.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/conftest.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/demo/__init__.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/demo/test_sim_motor.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/test_pattern_generator.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/test_sim_detector.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/test_sim_writer.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/test_streaming_plan.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/test_cli.py +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/test_data/test_yaml_save.yml +0 -0
- {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/test_log.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ophyd-async
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
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
|
|
@@ -41,7 +41,7 @@ Requires-Python: >=3.10
|
|
|
41
41
|
Description-Content-Type: text/markdown
|
|
42
42
|
License-File: LICENSE
|
|
43
43
|
Requires-Dist: networkx>=2.0
|
|
44
|
-
Requires-Dist: numpy
|
|
44
|
+
Requires-Dist: numpy<2.0.0
|
|
45
45
|
Requires-Dist: packaging
|
|
46
46
|
Requires-Dist: pint
|
|
47
47
|
Requires-Dist: bluesky>=1.13.0a3
|
|
@@ -31,7 +31,7 @@ from ophyd_async.protocols import AsyncConfigurable, AsyncReadable, AsyncStageab
|
|
|
31
31
|
from .async_status import AsyncStatus
|
|
32
32
|
from .device import Device
|
|
33
33
|
from .signal_backend import SignalBackend
|
|
34
|
-
from .soft_signal_backend import SoftSignalBackend
|
|
34
|
+
from .soft_signal_backend import SignalMetadata, SoftSignalBackend
|
|
35
35
|
from .utils import DEFAULT_TIMEOUT, CalculatableTimeout, CalculateTimeout, Callback, T
|
|
36
36
|
|
|
37
37
|
|
|
@@ -272,9 +272,17 @@ def soft_signal_rw(
|
|
|
272
272
|
datatype: Optional[Type[T]] = None,
|
|
273
273
|
initial_value: Optional[T] = None,
|
|
274
274
|
name: str = "",
|
|
275
|
+
units: str | None = None,
|
|
276
|
+
precision: int | None = None,
|
|
275
277
|
) -> SignalRW[T]:
|
|
276
|
-
"""Creates a read-writable Signal with a SoftSignalBackend
|
|
277
|
-
|
|
278
|
+
"""Creates a read-writable Signal with a SoftSignalBackend.
|
|
279
|
+
May pass metadata, which are propagated into describe.
|
|
280
|
+
"""
|
|
281
|
+
metadata = SignalMetadata(units=units, precision=precision)
|
|
282
|
+
signal = SignalRW(
|
|
283
|
+
SoftSignalBackend(datatype, initial_value, metadata=metadata),
|
|
284
|
+
name=name,
|
|
285
|
+
)
|
|
278
286
|
return signal
|
|
279
287
|
|
|
280
288
|
|
|
@@ -282,12 +290,16 @@ def soft_signal_r_and_setter(
|
|
|
282
290
|
datatype: Optional[Type[T]] = None,
|
|
283
291
|
initial_value: Optional[T] = None,
|
|
284
292
|
name: str = "",
|
|
293
|
+
units: str | None = None,
|
|
294
|
+
precision: int | None = None,
|
|
285
295
|
) -> Tuple[SignalR[T], Callable[[T], None]]:
|
|
286
296
|
"""Returns a tuple of a read-only Signal and a callable through
|
|
287
|
-
which the signal can be internally modified within the device.
|
|
288
|
-
|
|
297
|
+
which the signal can be internally modified within the device.
|
|
298
|
+
May pass metadata, which are propagated into describe.
|
|
299
|
+
Use soft_signal_rw if you want a device that is externally modifiable
|
|
289
300
|
"""
|
|
290
|
-
|
|
301
|
+
metadata = SignalMetadata(units=units, precision=precision)
|
|
302
|
+
backend = SoftSignalBackend(datatype, initial_value, metadata=metadata)
|
|
291
303
|
signal = SignalR(backend, name=name)
|
|
292
304
|
|
|
293
305
|
return (signal, backend.set_value)
|
|
@@ -5,7 +5,7 @@ import time
|
|
|
5
5
|
from collections import abc
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from enum import Enum
|
|
8
|
-
from typing import Dict, Generic, Optional, Type, Union, cast, get_origin
|
|
8
|
+
from typing import Dict, Generic, Optional, Type, TypedDict, Union, cast, get_origin
|
|
9
9
|
|
|
10
10
|
import numpy as np
|
|
11
11
|
from bluesky.protocols import DataKey, Dtype, Reading
|
|
@@ -21,6 +21,11 @@ primitive_dtypes: Dict[type, Dtype] = {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
class SignalMetadata(TypedDict):
|
|
25
|
+
units: str | None = None
|
|
26
|
+
precision: int | None = None
|
|
27
|
+
|
|
28
|
+
|
|
24
29
|
class SoftConverter(Generic[T]):
|
|
25
30
|
def value(self, value: T) -> T:
|
|
26
31
|
return value
|
|
@@ -35,7 +40,8 @@ class SoftConverter(Generic[T]):
|
|
|
35
40
|
alarm_severity=-1 if severity > 2 else severity,
|
|
36
41
|
)
|
|
37
42
|
|
|
38
|
-
def get_datakey(self, source: str, value) -> DataKey:
|
|
43
|
+
def get_datakey(self, source: str, value, **metadata) -> DataKey:
|
|
44
|
+
dk = {"source": source, "shape": [], **metadata}
|
|
39
45
|
dtype = type(value)
|
|
40
46
|
if np.issubdtype(dtype, np.integer):
|
|
41
47
|
dtype = int
|
|
@@ -44,8 +50,8 @@ class SoftConverter(Generic[T]):
|
|
|
44
50
|
assert (
|
|
45
51
|
dtype in primitive_dtypes
|
|
46
52
|
), f"invalid converter for value of type {type(value)}"
|
|
47
|
-
|
|
48
|
-
return
|
|
53
|
+
dk["dtype"] = primitive_dtypes[dtype]
|
|
54
|
+
return dk
|
|
49
55
|
|
|
50
56
|
def make_initial_value(self, datatype: Optional[Type[T]]) -> T:
|
|
51
57
|
if datatype is None:
|
|
@@ -55,8 +61,8 @@ class SoftConverter(Generic[T]):
|
|
|
55
61
|
|
|
56
62
|
|
|
57
63
|
class SoftArrayConverter(SoftConverter):
|
|
58
|
-
def get_datakey(self, source: str, value) -> DataKey:
|
|
59
|
-
return {"source": source, "dtype": "array", "shape": [len(value)]}
|
|
64
|
+
def get_datakey(self, source: str, value, **metadata) -> DataKey:
|
|
65
|
+
return {"source": source, "dtype": "array", "shape": [len(value)], **metadata}
|
|
60
66
|
|
|
61
67
|
def make_initial_value(self, datatype: Optional[Type[T]]) -> T:
|
|
62
68
|
if datatype is None:
|
|
@@ -78,9 +84,15 @@ class SoftEnumConverter(SoftConverter):
|
|
|
78
84
|
else:
|
|
79
85
|
return self.enum_class(value)
|
|
80
86
|
|
|
81
|
-
def get_datakey(self, source: str, value) -> DataKey:
|
|
87
|
+
def get_datakey(self, source: str, value, **metadata) -> DataKey:
|
|
82
88
|
choices = [e.value for e in self.enum_class]
|
|
83
|
-
return {
|
|
89
|
+
return {
|
|
90
|
+
"source": source,
|
|
91
|
+
"dtype": "string",
|
|
92
|
+
"shape": [],
|
|
93
|
+
"choices": choices,
|
|
94
|
+
**metadata,
|
|
95
|
+
}
|
|
84
96
|
|
|
85
97
|
def make_initial_value(self, datatype: Optional[Type[T]]) -> T:
|
|
86
98
|
if datatype is None:
|
|
@@ -114,9 +126,11 @@ class SoftSignalBackend(SignalBackend[T]):
|
|
|
114
126
|
self,
|
|
115
127
|
datatype: Optional[Type[T]],
|
|
116
128
|
initial_value: Optional[T] = None,
|
|
129
|
+
metadata: SignalMetadata = None,
|
|
117
130
|
) -> None:
|
|
118
131
|
self.datatype = datatype
|
|
119
132
|
self._initial_value = initial_value
|
|
133
|
+
self._metadata = metadata or {}
|
|
120
134
|
self.converter: SoftConverter = make_converter(datatype)
|
|
121
135
|
if self._initial_value is None:
|
|
122
136
|
self._initial_value = self.converter.make_initial_value(self.datatype)
|
|
@@ -155,7 +169,7 @@ class SoftSignalBackend(SignalBackend[T]):
|
|
|
155
169
|
self.callback(reading, self._value)
|
|
156
170
|
|
|
157
171
|
async def get_datakey(self, source: str) -> DataKey:
|
|
158
|
-
return self.converter.get_datakey(source, self._value)
|
|
172
|
+
return self.converter.get_datakey(source, self._value, **self._metadata)
|
|
159
173
|
|
|
160
174
|
async def get_reading(self) -> Reading:
|
|
161
175
|
return self.converter.reading(self._value, self._timestamp, self._severity)
|
|
@@ -88,9 +88,11 @@ def _data_key_from_augmented_value(
|
|
|
88
88
|
|
|
89
89
|
def _limits_from_augmented_value(value: AugmentedValue) -> Limits:
|
|
90
90
|
def get_limits(limit: str) -> LimitPair:
|
|
91
|
-
low = getattr(value, f"lower_{limit}_limit",
|
|
92
|
-
high = getattr(value, f"upper_{limit}_limit",
|
|
93
|
-
return LimitPair(
|
|
91
|
+
low = getattr(value, f"lower_{limit}_limit", nan)
|
|
92
|
+
high = getattr(value, f"upper_{limit}_limit", nan)
|
|
93
|
+
return LimitPair(
|
|
94
|
+
low=None if isnan(low) else low, high=None if isnan(high) else high
|
|
95
|
+
)
|
|
94
96
|
|
|
95
97
|
return Limits(
|
|
96
98
|
alarm=get_limits("alarm"),
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from enum import Enum
|
|
4
4
|
|
|
5
5
|
from ophyd_async.core import Device, DeviceVector, SignalR, SignalRW
|
|
6
|
-
from ophyd_async.panda._table import SeqTable
|
|
6
|
+
from ophyd_async.panda._table import DatasetTable, SeqTable
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class DataBlock(Device):
|
|
@@ -14,6 +14,7 @@ class DataBlock(Device):
|
|
|
14
14
|
num_captured: SignalR[int]
|
|
15
15
|
capture: SignalRW[bool]
|
|
16
16
|
flush_period: SignalRW[float]
|
|
17
|
+
datasets: SignalR[DatasetTable]
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class PulseBlock(Device):
|
|
@@ -28,9 +28,7 @@ class HDFPanda(CommonPandaBlocks, StandardDetector):
|
|
|
28
28
|
create_children_from_annotations(self)
|
|
29
29
|
controller = PandaPcapController(pcap=self.pcap)
|
|
30
30
|
writer = PandaHDFWriter(
|
|
31
|
-
prefix=prefix,
|
|
32
31
|
directory_provider=directory_provider,
|
|
33
|
-
name_provider=lambda: name,
|
|
34
32
|
panda_device=self,
|
|
35
33
|
)
|
|
36
34
|
super().__init__(
|
|
@@ -6,6 +6,16 @@ import numpy as np
|
|
|
6
6
|
import numpy.typing as npt
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
class PandaHdf5DatasetType(str, Enum):
|
|
10
|
+
FLOAT_64 = "float64"
|
|
11
|
+
UINT_32 = "uint32"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DatasetTable(TypedDict):
|
|
15
|
+
name: npt.NDArray[np.str_]
|
|
16
|
+
hdf5_type: Sequence[PandaHdf5DatasetType]
|
|
17
|
+
|
|
18
|
+
|
|
9
19
|
class SeqTrigger(str, Enum):
|
|
10
20
|
IMMEDIATE = "Immediate"
|
|
11
21
|
BITA_0 = "BITA=0"
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
from enum import Enum
|
|
4
2
|
from pathlib import Path
|
|
5
|
-
from typing import
|
|
3
|
+
from typing import AsyncGenerator, AsyncIterator, Dict, List, Optional
|
|
6
4
|
|
|
7
5
|
from bluesky.protocols import DataKey, StreamAsset
|
|
8
6
|
from p4p.client.thread import Context
|
|
@@ -10,98 +8,25 @@ from p4p.client.thread import Context
|
|
|
10
8
|
from ophyd_async.core import (
|
|
11
9
|
DEFAULT_TIMEOUT,
|
|
12
10
|
DetectorWriter,
|
|
13
|
-
Device,
|
|
14
11
|
DirectoryProvider,
|
|
15
|
-
NameProvider,
|
|
16
|
-
SignalR,
|
|
17
12
|
wait_for_value,
|
|
18
13
|
)
|
|
19
14
|
from ophyd_async.core.signal import observe_value
|
|
20
|
-
from ophyd_async.panda import CommonPandaBlocks
|
|
21
15
|
|
|
16
|
+
from .._common_blocks import CommonPandaBlocks
|
|
22
17
|
from ._panda_hdf_file import _HDFDataset, _HDFFile
|
|
23
18
|
|
|
24
19
|
|
|
25
|
-
class Capture(str, Enum):
|
|
26
|
-
# Capture signals for the HDF Panda
|
|
27
|
-
No = "No"
|
|
28
|
-
Value = "Value"
|
|
29
|
-
Diff = "Diff"
|
|
30
|
-
Sum = "Sum"
|
|
31
|
-
Mean = "Mean"
|
|
32
|
-
Min = "Min"
|
|
33
|
-
Max = "Max"
|
|
34
|
-
MinMax = "Min Max"
|
|
35
|
-
MinMaxMean = "Min Max Mean"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def get_capture_signals(
|
|
39
|
-
block: Device, path_prefix: Optional[str] = ""
|
|
40
|
-
) -> Dict[str, SignalR]:
|
|
41
|
-
"""Get dict mapping a capture signal's name to the signal itself"""
|
|
42
|
-
if not path_prefix:
|
|
43
|
-
path_prefix = ""
|
|
44
|
-
signals: Dict[str, SignalR[Any]] = {}
|
|
45
|
-
for attr_name, attr in block.children():
|
|
46
|
-
# Capture signals end in _capture, but num_capture is a red herring
|
|
47
|
-
if attr_name == "num_capture":
|
|
48
|
-
continue
|
|
49
|
-
dot_path = f"{path_prefix}{attr_name}"
|
|
50
|
-
if isinstance(attr, SignalR) and attr_name.endswith("_capture"):
|
|
51
|
-
signals[dot_path] = attr
|
|
52
|
-
attr_signals = get_capture_signals(attr, path_prefix=dot_path + ".")
|
|
53
|
-
signals.update(attr_signals)
|
|
54
|
-
return signals
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@dataclass
|
|
58
|
-
class CaptureSignalWrapper:
|
|
59
|
-
signal: SignalR
|
|
60
|
-
capture_type: Capture
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# This should return a dictionary which contains a dict, containing the Capture
|
|
64
|
-
# signal object, and the value of that signal
|
|
65
|
-
async def get_signals_marked_for_capture(
|
|
66
|
-
capture_signals: Dict[str, SignalR],
|
|
67
|
-
) -> Dict[str, CaptureSignalWrapper]:
|
|
68
|
-
# Read signals to see if they should be captured
|
|
69
|
-
do_read = [signal.get_value() for signal in capture_signals.values()]
|
|
70
|
-
|
|
71
|
-
signal_values = await asyncio.gather(*do_read)
|
|
72
|
-
|
|
73
|
-
assert len(signal_values) == len(
|
|
74
|
-
capture_signals
|
|
75
|
-
), "Length of read signals are different to length of signals"
|
|
76
|
-
|
|
77
|
-
signals_to_capture: Dict[str, CaptureSignalWrapper] = {}
|
|
78
|
-
for signal_path, signal_object, signal_value in zip(
|
|
79
|
-
capture_signals.keys(), capture_signals.values(), signal_values
|
|
80
|
-
):
|
|
81
|
-
signal_path = signal_path.replace("_capture", "")
|
|
82
|
-
if (signal_value in iter(Capture)) and (signal_value != Capture.No):
|
|
83
|
-
signals_to_capture[signal_path] = CaptureSignalWrapper(
|
|
84
|
-
signal_object,
|
|
85
|
-
signal_value,
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
return signals_to_capture
|
|
89
|
-
|
|
90
|
-
|
|
91
20
|
class PandaHDFWriter(DetectorWriter):
|
|
92
21
|
_ctxt: Optional[Context] = None
|
|
93
22
|
|
|
94
23
|
def __init__(
|
|
95
24
|
self,
|
|
96
|
-
prefix: str,
|
|
97
25
|
directory_provider: DirectoryProvider,
|
|
98
|
-
name_provider: NameProvider,
|
|
99
26
|
panda_device: CommonPandaBlocks,
|
|
100
27
|
) -> None:
|
|
101
28
|
self.panda_device = panda_device
|
|
102
|
-
self._prefix = prefix
|
|
103
29
|
self._directory_provider = directory_provider
|
|
104
|
-
self._name_provider = name_provider
|
|
105
30
|
self._datasets: List[_HDFDataset] = []
|
|
106
31
|
self._file: Optional[_HDFFile] = None
|
|
107
32
|
self._multiplier = 1
|
|
@@ -110,14 +35,9 @@ class PandaHDFWriter(DetectorWriter):
|
|
|
110
35
|
async def open(self, multiplier: int = 1) -> Dict[str, DataKey]:
|
|
111
36
|
"""Retrieve and get descriptor of all PandA signals marked for capture"""
|
|
112
37
|
|
|
113
|
-
# Get capture PVs by looking at panda. Gives mapping of dotted attribute path
|
|
114
|
-
# to Signal object
|
|
115
|
-
self.capture_signals = get_capture_signals(self.panda_device)
|
|
116
|
-
|
|
117
38
|
# Ensure flushes are immediate
|
|
118
39
|
await self.panda_device.data.flush_period.set(0)
|
|
119
40
|
|
|
120
|
-
to_capture = await get_signals_marked_for_capture(self.capture_signals)
|
|
121
41
|
self._file = None
|
|
122
42
|
info = self._directory_provider()
|
|
123
43
|
# Set the initial values
|
|
@@ -133,36 +53,21 @@ class PandaHDFWriter(DetectorWriter):
|
|
|
133
53
|
|
|
134
54
|
# Wait for it to start, stashing the status that tells us when it finishes
|
|
135
55
|
await self.panda_device.data.capture.set(True)
|
|
136
|
-
name = self._name_provider()
|
|
137
56
|
if multiplier > 1:
|
|
138
57
|
raise ValueError(
|
|
139
58
|
"All PandA datasets should be scalar, multiplier should be 1"
|
|
140
59
|
)
|
|
141
|
-
self._datasets = []
|
|
142
|
-
for attribute_path, capture_signal in to_capture.items():
|
|
143
|
-
split_path = attribute_path.split(".")
|
|
144
|
-
signal_name = split_path[-1]
|
|
145
|
-
# Get block names from numbered blocks, eg INENC[1]
|
|
146
|
-
block_name = (
|
|
147
|
-
f"{split_path[-3]}{split_path[-2]}"
|
|
148
|
-
if split_path[-2].isnumeric()
|
|
149
|
-
else split_path[-2]
|
|
150
|
-
)
|
|
151
60
|
|
|
152
|
-
|
|
153
|
-
self._datasets.append(
|
|
154
|
-
_HDFDataset(
|
|
155
|
-
name,
|
|
156
|
-
block_name,
|
|
157
|
-
f"{name}-{block_name}-{signal_name}-{suffix}",
|
|
158
|
-
f"{block_name}-{signal_name}".upper() + f"-{suffix}",
|
|
159
|
-
[1],
|
|
160
|
-
multiplier=1,
|
|
161
|
-
)
|
|
162
|
-
)
|
|
61
|
+
return await self._describe()
|
|
163
62
|
|
|
63
|
+
async def _describe(self) -> Dict[str, DataKey]:
|
|
64
|
+
"""
|
|
65
|
+
Return a describe based on the datasets PV
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
await self._update_datasets()
|
|
164
69
|
describe = {
|
|
165
|
-
ds.
|
|
70
|
+
ds.data_key: DataKey(
|
|
166
71
|
source=self.panda_device.data.hdf_directory.source,
|
|
167
72
|
shape=ds.shape,
|
|
168
73
|
dtype="array" if ds.shape != [1] else "number",
|
|
@@ -172,6 +77,18 @@ class PandaHDFWriter(DetectorWriter):
|
|
|
172
77
|
}
|
|
173
78
|
return describe
|
|
174
79
|
|
|
80
|
+
async def _update_datasets(self) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Load data from the datasets PV on the panda, update internal
|
|
83
|
+
representation of datasets that the panda will write.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
capture_table = await self.panda_device.data.datasets.get_value()
|
|
87
|
+
self._datasets = [
|
|
88
|
+
_HDFDataset(dataset_name, "/" + dataset_name, [1], multiplier=1)
|
|
89
|
+
for dataset_name in capture_table["name"]
|
|
90
|
+
]
|
|
91
|
+
|
|
175
92
|
# Next few functions are exactly the same as AD writer. Could move as default
|
|
176
93
|
# StandardDetector behavior
|
|
177
94
|
async def wait_for_index(
|
|
@@ -9,10 +9,8 @@ from ophyd_async.core import DirectoryInfo
|
|
|
9
9
|
|
|
10
10
|
@dataclass
|
|
11
11
|
class _HDFDataset:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
name: str
|
|
15
|
-
path: str
|
|
12
|
+
data_key: str
|
|
13
|
+
internal_path: str
|
|
16
14
|
shape: List[int]
|
|
17
15
|
multiplier: int
|
|
18
16
|
|
|
@@ -29,12 +27,10 @@ class _HDFFile:
|
|
|
29
27
|
compose_stream_resource(
|
|
30
28
|
spec="AD_HDF5_SWMR_SLICE",
|
|
31
29
|
root=str(directory_info.root),
|
|
32
|
-
data_key=ds.
|
|
30
|
+
data_key=ds.data_key,
|
|
33
31
|
resource_path=(f"{str(directory_info.root)}/{full_file_name}"),
|
|
34
32
|
resource_kwargs={
|
|
35
|
-
"
|
|
36
|
-
"block": ds.block,
|
|
37
|
-
"path": ds.path,
|
|
33
|
+
"path": ds.internal_path,
|
|
38
34
|
"multiplier": ds.multiplier,
|
|
39
35
|
"timestamps": "/entry/instrument/NDAttributes/NDArrayTimeStamp",
|
|
40
36
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ophyd-async
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
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
|
|
@@ -41,7 +41,7 @@ Requires-Python: >=3.10
|
|
|
41
41
|
Description-Content-Type: text/markdown
|
|
42
42
|
License-File: LICENSE
|
|
43
43
|
Requires-Dist: networkx>=2.0
|
|
44
|
-
Requires-Dist: numpy
|
|
44
|
+
Requires-Dist: numpy<2.0.0
|
|
45
45
|
Requires-Dist: packaging
|
|
46
46
|
Requires-Dist: pint
|
|
47
47
|
Requires-Dist: bluesky>=1.13.0a3
|
|
@@ -326,6 +326,38 @@ async def test_mock_signal_of_soft_signal_backend_receives_intial_value():
|
|
|
326
326
|
assert await soft_device.my_signal.get_value() == 10
|
|
327
327
|
|
|
328
328
|
|
|
329
|
+
async def test_mock_signal_of_soft_signal_backend_receives_metadata():
|
|
330
|
+
class SomeDevice(Device):
|
|
331
|
+
def __init__(self, name):
|
|
332
|
+
self.my_signal = soft_signal_rw(
|
|
333
|
+
datatype=float, initial_value=1.0, name=name, units="mm", precision=2
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
mocked_device = SomeDevice("mocked_device")
|
|
337
|
+
await mocked_device.connect(mock=True)
|
|
338
|
+
soft_device = SomeDevice("soft_device")
|
|
339
|
+
await soft_device.connect(mock=False)
|
|
340
|
+
|
|
341
|
+
assert await mocked_device.my_signal.describe() == {
|
|
342
|
+
"mocked_device": {
|
|
343
|
+
"dtype": "number",
|
|
344
|
+
"shape": [],
|
|
345
|
+
"source": "mock+soft://mocked_device",
|
|
346
|
+
"units": "mm",
|
|
347
|
+
"precision": 2,
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
assert await soft_device.my_signal.describe() == {
|
|
351
|
+
"soft_device": {
|
|
352
|
+
"dtype": "number",
|
|
353
|
+
"shape": [],
|
|
354
|
+
"source": "soft://soft_device",
|
|
355
|
+
"units": "mm",
|
|
356
|
+
"precision": 2,
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
|
|
329
361
|
async def test_writing_to_soft_signals_in_mock():
|
|
330
362
|
class MyDevice(Device):
|
|
331
363
|
def __init__(self, prefix: str, name: str = ""):
|
|
@@ -9,7 +9,7 @@ import pytest
|
|
|
9
9
|
from bluesky.protocols import Reading
|
|
10
10
|
|
|
11
11
|
from ophyd_async.core import Signal, SignalBackend, T
|
|
12
|
-
from ophyd_async.core.soft_signal_backend import SoftSignalBackend
|
|
12
|
+
from ophyd_async.core.soft_signal_backend import SignalMetadata, SoftSignalBackend
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class MyEnum(str, Enum):
|
|
@@ -133,3 +133,39 @@ async def test_soft_signal_descriptor_fails_for_invalid_class():
|
|
|
133
133
|
|
|
134
134
|
with pytest.raises(AssertionError):
|
|
135
135
|
await soft_signal._backend.get_datakey("")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
async def test_soft_signal_descriptor_with_metadata():
|
|
139
|
+
soft_signal = Signal(
|
|
140
|
+
SoftSignalBackend(int, 0, metadata=SignalMetadata(units="mm", precision=0))
|
|
141
|
+
)
|
|
142
|
+
await soft_signal.connect()
|
|
143
|
+
datakey = await soft_signal._backend.get_datakey("")
|
|
144
|
+
assert datakey["units"] == "mm"
|
|
145
|
+
assert datakey["precision"] == 0
|
|
146
|
+
|
|
147
|
+
soft_signal = Signal(SoftSignalBackend(int, metadata=SignalMetadata(units="")))
|
|
148
|
+
await soft_signal.connect()
|
|
149
|
+
datakey = await soft_signal._backend.get_datakey("")
|
|
150
|
+
assert datakey["units"] == ""
|
|
151
|
+
assert not hasattr(datakey, "precision")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
async def test_soft_signal_descriptor_with_no_metadata_not_passed():
|
|
155
|
+
soft_signal = Signal(SoftSignalBackend(int))
|
|
156
|
+
await soft_signal.connect()
|
|
157
|
+
datakey = await soft_signal._backend.get_datakey("")
|
|
158
|
+
assert not hasattr(datakey, "units")
|
|
159
|
+
assert not hasattr(datakey, "precision")
|
|
160
|
+
|
|
161
|
+
soft_signal = Signal(SoftSignalBackend(int, metadata=None))
|
|
162
|
+
await soft_signal.connect()
|
|
163
|
+
datakey = await soft_signal._backend.get_datakey("")
|
|
164
|
+
assert not hasattr(datakey, "units")
|
|
165
|
+
assert not hasattr(datakey, "precision")
|
|
166
|
+
|
|
167
|
+
soft_signal = Signal(SoftSignalBackend(int, metadata={}))
|
|
168
|
+
await soft_signal.connect()
|
|
169
|
+
datakey = await soft_signal._backend.get_datakey("")
|
|
170
|
+
assert not hasattr(datakey, "units")
|
|
171
|
+
assert not hasattr(datakey, "precision")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from typing import Dict
|
|
2
2
|
|
|
3
|
+
import numpy as np
|
|
3
4
|
import pytest
|
|
4
5
|
from bluesky import plan_stubs as bps
|
|
5
6
|
from bluesky.run_engine import RunEngine
|
|
@@ -9,9 +10,8 @@ from ophyd_async.core.device import Device
|
|
|
9
10
|
from ophyd_async.core.flyer import HardwareTriggeredFlyable
|
|
10
11
|
from ophyd_async.core.mock_signal_utils import callback_on_mock_put
|
|
11
12
|
from ophyd_async.core.signal import SignalR, assert_emitted
|
|
12
|
-
from ophyd_async.epics.signal.signal import epics_signal_r
|
|
13
13
|
from ophyd_async.panda import HDFPanda, StaticSeqTableTriggerLogic
|
|
14
|
-
from ophyd_async.panda.
|
|
14
|
+
from ophyd_async.panda._table import DatasetTable, PandaHdf5DatasetType
|
|
15
15
|
from ophyd_async.plan_stubs import (
|
|
16
16
|
prepare_static_seq_table_flyer_and_detectors_with_same_trigger,
|
|
17
17
|
)
|
|
@@ -26,25 +26,28 @@ async def mock_hdf_panda(tmp_path):
|
|
|
26
26
|
mock_hdf_panda = HDFPanda(
|
|
27
27
|
"HDFPANDA:", directory_provider=directory_provider, name="panda"
|
|
28
28
|
)
|
|
29
|
-
block_a = CaptureBlock(name="block_a")
|
|
30
|
-
block_b = CaptureBlock(name="block_b")
|
|
31
|
-
block_a.test_capture = epics_signal_r(
|
|
32
|
-
Capture, "pva://test_capture_a", name="test_capture_a"
|
|
33
|
-
)
|
|
34
|
-
block_b.test_capture = epics_signal_r(
|
|
35
|
-
Capture, "pva://test_capture_b", name="test_capture_b"
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
setattr(mock_hdf_panda, "block_a", block_a)
|
|
39
|
-
setattr(mock_hdf_panda, "block_b", block_b)
|
|
40
29
|
await mock_hdf_panda.connect(mock=True)
|
|
41
30
|
|
|
42
31
|
def link_function(value, **kwargs):
|
|
43
32
|
set_mock_value(mock_hdf_panda.pcap.active, value)
|
|
44
33
|
|
|
45
34
|
callback_on_mock_put(mock_hdf_panda.pcap.arm, link_function)
|
|
46
|
-
|
|
47
|
-
set_mock_value(
|
|
35
|
+
|
|
36
|
+
set_mock_value(
|
|
37
|
+
mock_hdf_panda.data.datasets,
|
|
38
|
+
DatasetTable(
|
|
39
|
+
name=np.array(
|
|
40
|
+
[
|
|
41
|
+
"x",
|
|
42
|
+
"y",
|
|
43
|
+
]
|
|
44
|
+
),
|
|
45
|
+
hdf5_type=[
|
|
46
|
+
PandaHdf5DatasetType.UINT_32,
|
|
47
|
+
PandaHdf5DatasetType.FLOAT_64,
|
|
48
|
+
],
|
|
49
|
+
),
|
|
50
|
+
)
|
|
48
51
|
|
|
49
52
|
yield mock_hdf_panda
|
|
50
53
|
|
|
@@ -127,10 +130,7 @@ async def test_hdf_panda_hardware_triggered_flyable(
|
|
|
127
130
|
|
|
128
131
|
# test descriptor
|
|
129
132
|
data_key_names: Dict[str, str] = docs["descriptor"][0]["object_keys"]["panda"]
|
|
130
|
-
assert data_key_names == [
|
|
131
|
-
"panda-block_a-test-Min",
|
|
132
|
-
"panda-block_b-test-Diff",
|
|
133
|
-
]
|
|
133
|
+
assert data_key_names == ["x", "y"]
|
|
134
134
|
for data_key_name in data_key_names:
|
|
135
135
|
assert (
|
|
136
136
|
docs["descriptor"][0]["data_keys"][data_key_name]["source"]
|
|
@@ -138,17 +138,15 @@ async def test_hdf_panda_hardware_triggered_flyable(
|
|
|
138
138
|
)
|
|
139
139
|
|
|
140
140
|
# test stream resources
|
|
141
|
-
for
|
|
142
|
-
("
|
|
141
|
+
for dataset_name, stream_resource, data_key_name in zip(
|
|
142
|
+
("x", "y"), docs["stream_resource"], data_key_names
|
|
143
143
|
):
|
|
144
144
|
assert stream_resource["data_key"] == data_key_name
|
|
145
145
|
assert stream_resource["spec"] == "AD_HDF5_SWMR_SLICE"
|
|
146
146
|
assert stream_resource["run_start"] == docs["start"][0]["uid"]
|
|
147
147
|
assert stream_resource["resource_kwargs"] == {
|
|
148
|
-
"
|
|
148
|
+
"path": "/" + dataset_name,
|
|
149
149
|
"multiplier": 1,
|
|
150
|
-
"name": data_key_name,
|
|
151
|
-
"path": f"BLOCK_{block_letter.upper()}-TEST-{data_key_name.split('-')[-1]}",
|
|
152
150
|
"timestamps": "/entry/instrument/NDAttributes/NDArrayTimeStamp",
|
|
153
151
|
}
|
|
154
152
|
|