ophyd-async 0.3.2__tar.gz → 0.3.4a1__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.
Files changed (203) hide show
  1. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/PKG-INFO +1 -1
  2. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/_version.py +2 -2
  3. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/__init__.py +47 -44
  4. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/signal_backend.py +47 -1
  5. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/soft_signal_backend.py +29 -12
  6. ophyd_async-0.3.4a1/src/ophyd_async/epics/_backend/common.py +62 -0
  7. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/controllers/pilatus_controller.py +4 -1
  8. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/drivers/__init__.py +2 -0
  9. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/drivers/ad_base.py +32 -0
  10. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/drivers/aravis_driver.py +5 -3
  11. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/motion/motor.py +0 -2
  12. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async.egg-info/PKG-INFO +1 -1
  13. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async.egg-info/SOURCES.txt +1 -0
  14. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/core/test_soft_signal_backend.py +1 -1
  15. ophyd_async-0.3.4a1/tests/core/test_subset_enum.py +90 -0
  16. ophyd_async-0.3.4a1/tests/epics/areadetector/test_drivers.py +96 -0
  17. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/areadetector/test_pilatus.py +8 -1
  18. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/demo/test_demo.py +1 -1
  19. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/test_signals.py +27 -7
  20. ophyd_async-0.3.2/src/ophyd_async/epics/_backend/common.py +0 -48
  21. ophyd_async-0.3.2/tests/epics/areadetector/test_drivers.py +0 -47
  22. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.codecov.yml +0 -0
  23. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.copier-answers.yml +0 -0
  24. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.devcontainer/devcontainer.json +0 -0
  25. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.git-blame-ignore-revs +0 -0
  26. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/CONTRIBUTING.md +0 -0
  27. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/actions/install_requirements/action.yml +0 -0
  28. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/dependabot.yml +0 -0
  29. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/pages/index.html +0 -0
  30. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/pages/make_switcher.py +0 -0
  31. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/workflows/_check.yml +0 -0
  32. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/workflows/_dist.yml +0 -0
  33. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/workflows/_docs.yml +0 -0
  34. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/workflows/_pypi.yml +0 -0
  35. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/workflows/_release.yml +0 -0
  36. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/workflows/_test.yml +0 -0
  37. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/workflows/_tox.yml +0 -0
  38. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/workflows/ci.yml +0 -0
  39. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.github/workflows/periodic.yml +0 -0
  40. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.gitignore +0 -0
  41. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.mailmap +0 -0
  42. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/.pre-commit-config.yaml +0 -0
  43. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/Dockerfile +0 -0
  44. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/LICENSE +0 -0
  45. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/README.md +0 -0
  46. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/_templates/README +0 -0
  47. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/_templates/custom-class-template.rst +0 -0
  48. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/_templates/custom-module-template.rst +0 -0
  49. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/conf.py +0 -0
  50. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/examples/epics_demo.py +0 -0
  51. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/examples/foo_detector.py +0 -0
  52. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/explanations/decisions/0001-record-architecture-decisions.md +0 -0
  53. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/explanations/decisions/0002-switched-to-python-copier-template.md +0 -0
  54. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/explanations/decisions/0003-ophyd-async-migration.rst +0 -0
  55. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/explanations/decisions/0004-repository-structure.rst +0 -0
  56. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/explanations/decisions/0005-respect-black-line-length.rst +0 -0
  57. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/explanations/decisions/0006-procedural-device-definitions.rst +0 -0
  58. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/explanations/decisions/COPYME +0 -0
  59. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/explanations/decisions.md +0 -0
  60. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/explanations/design-goals.rst +0 -0
  61. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/explanations/event-loop-choice.rst +0 -0
  62. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/explanations/flyscanning.rst +0 -0
  63. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/explanations.md +0 -0
  64. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/genindex.rst +0 -0
  65. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/how-to/choose-interfaces-for-devices.md +0 -0
  66. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/how-to/compound-devices.rst +0 -0
  67. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/how-to/contribute.md +0 -0
  68. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/how-to/make-a-simple-device.rst +0 -0
  69. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/how-to/make-a-standard-detector.rst +0 -0
  70. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/how-to/write-tests-for-devices.rst +0 -0
  71. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/how-to.md +0 -0
  72. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/images/bluesky_ophyd_epics_devices_logo.svg +0 -0
  73. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/images/bluesky_ophyd_logo.svg +0 -0
  74. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/images/ophyd_favicon.svg +0 -0
  75. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/index.md +0 -0
  76. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/reference/api.rst +0 -0
  77. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/reference.md +0 -0
  78. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/tutorials/installation.md +0 -0
  79. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/tutorials/using-existing-devices.rst +0 -0
  80. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/docs/tutorials.md +0 -0
  81. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/pyproject.toml +0 -0
  82. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/setup.cfg +0 -0
  83. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/__init__.py +0 -0
  84. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/__main__.py +0 -0
  85. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/_providers.py +0 -0
  86. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/async_status.py +0 -0
  87. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/detector.py +0 -0
  88. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/device.py +0 -0
  89. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/device_save_loader.py +0 -0
  90. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/flyer.py +0 -0
  91. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/mock_signal_backend.py +0 -0
  92. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/mock_signal_utils.py +0 -0
  93. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/signal.py +0 -0
  94. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/standard_readable.py +0 -0
  95. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/core/utils.py +0 -0
  96. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/__init__.py +0 -0
  97. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/_backend/__init__.py +0 -0
  98. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/_backend/_aioca.py +0 -0
  99. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/_backend/_p4p.py +0 -0
  100. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/__init__.py +0 -0
  101. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/aravis.py +0 -0
  102. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/controllers/__init__.py +0 -0
  103. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/controllers/ad_sim_controller.py +0 -0
  104. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/controllers/aravis_controller.py +0 -0
  105. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/controllers/kinetix_controller.py +0 -0
  106. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/controllers/vimba_controller.py +0 -0
  107. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/drivers/kinetix_driver.py +0 -0
  108. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/drivers/pilatus_driver.py +0 -0
  109. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/drivers/vimba_driver.py +0 -0
  110. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/kinetix.py +0 -0
  111. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/pilatus.py +0 -0
  112. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/single_trigger_det.py +0 -0
  113. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/utils.py +0 -0
  114. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/vimba.py +0 -0
  115. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/writers/__init__.py +0 -0
  116. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/writers/_hdfdataset.py +0 -0
  117. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/writers/_hdffile.py +0 -0
  118. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/writers/hdf_writer.py +0 -0
  119. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/writers/nd_file_hdf.py +0 -0
  120. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/areadetector/writers/nd_plugin.py +0 -0
  121. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/demo/__init__.py +0 -0
  122. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/demo/demo_ad_sim_detector.py +0 -0
  123. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/demo/mover.db +0 -0
  124. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/demo/sensor.db +0 -0
  125. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/motion/__init__.py +0 -0
  126. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/pvi/__init__.py +0 -0
  127. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/pvi/pvi.py +0 -0
  128. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/signal/__init__.py +0 -0
  129. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/signal/_epics_transport.py +0 -0
  130. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/epics/signal/signal.py +0 -0
  131. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/log.py +0 -0
  132. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/panda/__init__.py +0 -0
  133. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/panda/_common_blocks.py +0 -0
  134. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/panda/_hdf_panda.py +0 -0
  135. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/panda/_panda_controller.py +0 -0
  136. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/panda/_table.py +0 -0
  137. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/panda/_trigger.py +0 -0
  138. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/panda/_utils.py +0 -0
  139. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/panda/writers/__init__.py +0 -0
  140. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/panda/writers/_hdf_writer.py +0 -0
  141. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/panda/writers/_panda_hdf_file.py +0 -0
  142. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/plan_stubs/__init__.py +0 -0
  143. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/plan_stubs/ensure_connected.py +0 -0
  144. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/plan_stubs/fly.py +0 -0
  145. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/protocols.py +0 -0
  146. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/sim/__init__.py +0 -0
  147. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/sim/demo/__init__.py +0 -0
  148. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/sim/demo/sim_motor.py +0 -0
  149. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/sim/pattern_generator.py +0 -0
  150. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/sim/sim_pattern_detector_control.py +0 -0
  151. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/sim/sim_pattern_detector_writer.py +0 -0
  152. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async/sim/sim_pattern_generator.py +0 -0
  153. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async.egg-info/dependency_links.txt +0 -0
  154. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async.egg-info/entry_points.txt +0 -0
  155. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async.egg-info/requires.txt +0 -0
  156. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/src/ophyd_async.egg-info/top_level.txt +0 -0
  157. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/conftest.py +0 -0
  158. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/core/test_async_status.py +0 -0
  159. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/core/test_device.py +0 -0
  160. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/core/test_device_collector.py +0 -0
  161. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/core/test_device_save_loader.py +0 -0
  162. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/core/test_flyer.py +0 -0
  163. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/core/test_mock_signal_backend.py +0 -0
  164. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/core/test_signal.py +0 -0
  165. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/core/test_standard_readable.py +0 -0
  166. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/core/test_utils.py +0 -0
  167. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/core/test_watchable_async_status.py +0 -0
  168. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/_backend/test_common.py +0 -0
  169. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/areadetector/__init__.py +0 -0
  170. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/areadetector/test_aravis.py +0 -0
  171. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/areadetector/test_controllers.py +0 -0
  172. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/areadetector/test_kinetix.py +0 -0
  173. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/areadetector/test_scans.py +0 -0
  174. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/areadetector/test_single_trigger_det.py +0 -0
  175. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/areadetector/test_utils.py +0 -0
  176. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/areadetector/test_vimba.py +0 -0
  177. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/areadetector/test_writers.py +0 -0
  178. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/demo/test_demo_ad_sim_detector.py +0 -0
  179. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/motion/__init__.py +0 -0
  180. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/motion/test_motor.py +0 -0
  181. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/test_pvi.py +0 -0
  182. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/epics/test_records.db +0 -0
  183. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/panda/db/panda.db +0 -0
  184. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/panda/test_hdf_panda.py +0 -0
  185. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/panda/test_panda_connect.py +0 -0
  186. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/panda/test_panda_controller.py +0 -0
  187. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/panda/test_panda_utils.py +0 -0
  188. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/panda/test_table.py +0 -0
  189. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/panda/test_trigger.py +0 -0
  190. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/panda/test_writer.py +0 -0
  191. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/plan_stubs/test_fly.py +0 -0
  192. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/protocols/test_protocols.py +0 -0
  193. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/sim/__init__.py +0 -0
  194. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/sim/conftest.py +0 -0
  195. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/sim/demo/__init__.py +0 -0
  196. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/sim/demo/test_sim_motor.py +0 -0
  197. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/sim/test_pattern_generator.py +0 -0
  198. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/sim/test_sim_detector.py +0 -0
  199. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/sim/test_sim_writer.py +0 -0
  200. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/sim/test_streaming_plan.py +0 -0
  201. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/test_cli.py +0 -0
  202. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/tests/test_data/test_yaml_save.yml +0 -0
  203. {ophyd_async-0.3.2 → ophyd_async-0.3.4a1}/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.2
3
+ Version: 0.3.4a1
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
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.3.2'
16
- __version_tuple__ = version_tuple = (0, 3, 2)
15
+ __version__ = version = '0.3.4a1'
16
+ __version_tuple__ = version_tuple = (0, 3, 4)
@@ -50,7 +50,7 @@ from .signal import (
50
50
  soft_signal_rw,
51
51
  wait_for_value,
52
52
  )
53
- from .signal_backend import SignalBackend
53
+ from .signal_backend import RuntimeSubsetEnum, SignalBackend, SubsetEnum
54
54
  from .soft_signal_backend import SoftSignalBackend
55
55
  from .standard_readable import ConfigSignal, HintedSignal, StandardReadable
56
56
  from .utils import (
@@ -68,66 +68,69 @@ from .utils import (
68
68
  )
69
69
 
70
70
  __all__ = [
71
- "get_mock_put",
72
- "callback_on_mock_put",
73
- "mock_puts_blocked",
74
- "set_mock_values",
75
- "reset_mock_put_calls",
76
- "SignalBackend",
77
- "SoftSignalBackend",
71
+ "AsyncStatus",
72
+ "CalculatableTimeout",
73
+ "CalculateTimeout",
74
+ "Callback",
75
+ "ConfigSignal",
76
+ "DEFAULT_TIMEOUT",
78
77
  "DetectorControl",
79
- "MockSignalBackend",
80
78
  "DetectorTrigger",
81
79
  "DetectorWriter",
82
- "StandardDetector",
83
80
  "Device",
84
81
  "DeviceCollector",
85
82
  "DeviceVector",
86
- "Signal",
87
- "SignalR",
88
- "SignalW",
89
- "SignalRW",
90
- "SignalX",
91
- "soft_signal_r_and_setter",
92
- "soft_signal_rw",
93
- "observe_value",
94
- "set_and_wait_for_value",
95
- "set_mock_put_proceeds",
96
- "set_mock_value",
97
- "wait_for_value",
98
- "AsyncStatus",
99
- "WatchableAsyncStatus",
100
83
  "DirectoryInfo",
101
84
  "DirectoryProvider",
85
+ "HardwareTriggeredFlyable",
86
+ "HintedSignal",
87
+ "MockSignalBackend",
102
88
  "NameProvider",
89
+ "NotConnected",
90
+ "ReadingValueCallback",
91
+ "RuntimeSubsetEnum",
92
+ "SubsetEnum",
103
93
  "ShapeProvider",
104
- "StaticDirectoryProvider",
94
+ "Signal",
95
+ "SignalBackend",
96
+ "SignalR",
97
+ "SignalRW",
98
+ "SignalW",
99
+ "SignalX",
100
+ "SoftSignalBackend",
101
+ "StandardDetector",
105
102
  "StandardReadable",
106
- "ConfigSignal",
107
- "HintedSignal",
103
+ "StaticDirectoryProvider",
104
+ "T",
108
105
  "TriggerInfo",
109
106
  "TriggerLogic",
110
- "HardwareTriggeredFlyable",
111
- "CalculateTimeout",
112
- "CalculatableTimeout",
113
- "DEFAULT_TIMEOUT",
114
- "Callback",
115
- "NotConnected",
116
- "ReadingValueCallback",
117
- "T",
107
+ "WatchableAsyncStatus",
108
+ "assert_configuration",
109
+ "assert_emitted",
110
+ "assert_mock_put_called_with",
111
+ "assert_reading",
112
+ "assert_value",
113
+ "callback_on_mock_put",
118
114
  "get_dtype",
119
- "get_unique",
120
- "merge_gathered_dicts",
121
- "wait_for_connection",
115
+ "get_mock_put",
122
116
  "get_signal_values",
117
+ "get_unique",
118
+ "load_device",
123
119
  "load_from_yaml",
120
+ "merge_gathered_dicts",
121
+ "mock_puts_blocked",
122
+ "observe_value",
123
+ "reset_mock_put_calls",
124
+ "save_device",
124
125
  "save_to_yaml",
126
+ "set_and_wait_for_value",
127
+ "set_mock_put_proceeds",
128
+ "set_mock_value",
129
+ "set_mock_values",
125
130
  "set_signal_values",
131
+ "soft_signal_r_and_setter",
132
+ "soft_signal_rw",
133
+ "wait_for_connection",
134
+ "wait_for_value",
126
135
  "walk_rw_signals",
127
- "load_device",
128
- "save_device",
129
- "assert_reading",
130
- "assert_value",
131
- "assert_configuration",
132
- "assert_emitted",
133
136
  ]
@@ -1,5 +1,13 @@
1
1
  from abc import abstractmethod
2
- from typing import Generic, Optional, Type
2
+ from typing import (
3
+ TYPE_CHECKING,
4
+ ClassVar,
5
+ Generic,
6
+ Literal,
7
+ Optional,
8
+ Tuple,
9
+ Type,
10
+ )
3
11
 
4
12
  from bluesky.protocols import DataKey, Reading
5
13
 
@@ -45,3 +53,41 @@ class SignalBackend(Generic[T]):
45
53
  @abstractmethod
46
54
  def set_callback(self, callback: Optional[ReadingValueCallback[T]]) -> None:
47
55
  """Observe changes to the current value, timestamp and severity"""
56
+
57
+
58
+ class _RuntimeSubsetEnumMeta(type):
59
+ def __str__(cls):
60
+ if hasattr(cls, "choices"):
61
+ return f"SubsetEnum{list(cls.choices)}"
62
+ return "SubsetEnum"
63
+
64
+ def __getitem__(cls, _choices):
65
+ if isinstance(_choices, str):
66
+ _choices = (_choices,)
67
+ else:
68
+ if not isinstance(_choices, tuple) or not all(
69
+ isinstance(c, str) for c in _choices
70
+ ):
71
+ raise TypeError(
72
+ "Choices must be a str or a tuple of str, " f"not {type(_choices)}."
73
+ )
74
+ if len(set(_choices)) != len(_choices):
75
+ raise TypeError("Duplicate elements in runtime enum choices.")
76
+
77
+ class _RuntimeSubsetEnum(cls):
78
+ choices = _choices
79
+
80
+ return _RuntimeSubsetEnum
81
+
82
+
83
+ class RuntimeSubsetEnum(metaclass=_RuntimeSubsetEnumMeta):
84
+ choices: ClassVar[Tuple[str, ...]]
85
+
86
+ def __init__(self):
87
+ raise RuntimeError("SubsetEnum cannot be instantiated")
88
+
89
+
90
+ if TYPE_CHECKING:
91
+ SubsetEnum = Literal
92
+ else:
93
+ SubsetEnum = RuntimeSubsetEnum
@@ -3,14 +3,23 @@ from __future__ import annotations
3
3
  import inspect
4
4
  import time
5
5
  from collections import abc
6
- from dataclasses import dataclass
7
6
  from enum import Enum
8
- from typing import Dict, Generic, Optional, Type, TypedDict, Union, cast, get_origin
7
+ from typing import (
8
+ Dict,
9
+ Generic,
10
+ Optional,
11
+ Tuple,
12
+ Type,
13
+ TypedDict,
14
+ Union,
15
+ cast,
16
+ get_origin,
17
+ )
9
18
 
10
19
  import numpy as np
11
20
  from bluesky.protocols import DataKey, Dtype, Reading
12
21
 
13
- from .signal_backend import SignalBackend
22
+ from .signal_backend import RuntimeSubsetEnum, SignalBackend
14
23
  from .utils import DEFAULT_TIMEOUT, ReadingValueCallback, T, get_dtype
15
24
 
16
25
  primitive_dtypes: Dict[type, Dtype] = {
@@ -74,23 +83,27 @@ class SoftArrayConverter(SoftConverter):
74
83
  return cast(T, datatype(shape=0)) # type: ignore
75
84
 
76
85
 
77
- @dataclass
78
86
  class SoftEnumConverter(SoftConverter):
79
- enum_class: Type[Enum]
87
+ choices: Tuple[str, ...]
80
88
 
81
- def write_value(self, value: Union[Enum, str]) -> Enum:
89
+ def __init__(self, datatype: Union[RuntimeSubsetEnum, Enum]):
90
+ if issubclass(datatype, Enum):
91
+ self.choices = tuple(v.value for v in datatype)
92
+ else:
93
+ self.choices = datatype.choices
94
+
95
+ def write_value(self, value: Union[Enum, str]) -> str:
82
96
  if isinstance(value, Enum):
97
+ return value.value
98
+ else: # Runtime enum
83
99
  return value
84
- else:
85
- return self.enum_class(value)
86
100
 
87
101
  def get_datakey(self, source: str, value, **metadata) -> DataKey:
88
- choices = [e.value for e in self.enum_class]
89
102
  return {
90
103
  "source": source,
91
104
  "dtype": "string",
92
105
  "shape": [],
93
- "choices": choices,
106
+ "choices": self.choices,
94
107
  **metadata,
95
108
  }
96
109
 
@@ -98,13 +111,17 @@ class SoftEnumConverter(SoftConverter):
98
111
  if datatype is None:
99
112
  return cast(T, None)
100
113
 
101
- return cast(T, list(datatype.__members__.values())[0]) # type: ignore
114
+ if issubclass(datatype, Enum):
115
+ return cast(T, list(datatype.__members__.values())[0]) # type: ignore
116
+ return cast(T, self.choices[0])
102
117
 
103
118
 
104
119
  def make_converter(datatype):
105
120
  is_array = get_dtype(datatype) is not None
106
121
  is_sequence = get_origin(datatype) == abc.Sequence
107
- is_enum = issubclass(datatype, Enum) if inspect.isclass(datatype) else False
122
+ is_enum = inspect.isclass(datatype) and (
123
+ issubclass(datatype, Enum) or issubclass(datatype, RuntimeSubsetEnum)
124
+ )
108
125
 
109
126
  if is_array or is_sequence:
110
127
  return SoftArrayConverter()
@@ -0,0 +1,62 @@
1
+ import inspect
2
+ from enum import Enum
3
+ from typing import Dict, Optional, Tuple, Type, TypedDict
4
+
5
+ from ophyd_async.core.signal_backend import RuntimeSubsetEnum
6
+
7
+ common_meta = {
8
+ "units",
9
+ "precision",
10
+ }
11
+
12
+
13
+ class LimitPair(TypedDict):
14
+ high: float | None
15
+ low: float | None
16
+
17
+ def __bool__(self) -> bool:
18
+ return self.low is None and self.high is None
19
+
20
+
21
+ class Limits(TypedDict):
22
+ alarm: LimitPair
23
+ control: LimitPair
24
+ display: LimitPair
25
+ warning: LimitPair
26
+
27
+ def __bool__(self) -> bool:
28
+ return any(self.alarm, self.control, self.display, self.warning)
29
+
30
+
31
+ def get_supported_values(
32
+ pv: str,
33
+ datatype: Optional[Type[str]],
34
+ pv_choices: Tuple[str, ...],
35
+ ) -> Dict[str, str]:
36
+ if inspect.isclass(datatype) and issubclass(datatype, RuntimeSubsetEnum):
37
+ if not set(datatype.choices).issubset(set(pv_choices)):
38
+ raise TypeError(
39
+ f"{pv} has choices {pv_choices}, "
40
+ f"which is not a superset of {str(datatype)}."
41
+ )
42
+ return {x: x or "_" for x in pv_choices}
43
+ elif inspect.isclass(datatype) and issubclass(datatype, Enum):
44
+ if not issubclass(datatype, str):
45
+ raise TypeError(
46
+ f"{pv} is type Enum but {datatype} does not inherit from String."
47
+ )
48
+
49
+ choices = tuple(v.value for v in datatype)
50
+ if set(choices) != set(pv_choices):
51
+ raise TypeError(
52
+ f"{pv} has choices {pv_choices}, "
53
+ f"which do not match {datatype}, which has {choices}."
54
+ )
55
+ return {x: datatype(x) if x else "_" for x in pv_choices}
56
+ elif datatype is None:
57
+ return {x: x or "_" for x in pv_choices}
58
+
59
+ raise TypeError(
60
+ f"{pv} has choices {pv_choices}. "
61
+ "Use an Enum or SubsetEnum to represent this."
62
+ )
@@ -5,6 +5,7 @@ from ophyd_async.core import DEFAULT_TIMEOUT, wait_for_value
5
5
  from ophyd_async.core.async_status import AsyncStatus
6
6
  from ophyd_async.core.detector import DetectorControl, DetectorTrigger
7
7
  from ophyd_async.epics.areadetector.drivers.ad_base import (
8
+ set_exposure_time_and_acquire_period_if_supplied,
8
9
  start_acquiring_driver_and_ensure_status,
9
10
  )
10
11
  from ophyd_async.epics.areadetector.drivers.pilatus_driver import (
@@ -39,7 +40,9 @@ class PilatusController(DetectorControl):
39
40
  exposure: Optional[float] = None,
40
41
  ) -> AsyncStatus:
41
42
  if exposure is not None:
42
- await self._drv.acquire_time.set(exposure)
43
+ await set_exposure_time_and_acquire_period_if_supplied(
44
+ self, self._drv, exposure
45
+ )
43
46
  await asyncio.gather(
44
47
  self._drv.trigger_mode.set(self._get_trigger_mode(trigger)),
45
48
  self._drv.num_images.set(999_999 if num == 0 else num),
@@ -2,6 +2,7 @@ from .ad_base import (
2
2
  ADBase,
3
3
  ADBaseShapeProvider,
4
4
  DetectorState,
5
+ set_exposure_time_and_acquire_period_if_supplied,
5
6
  start_acquiring_driver_and_ensure_status,
6
7
  )
7
8
  from .aravis_driver import AravisDriver
@@ -17,5 +18,6 @@ __all__ = [
17
18
  "KinetixDriver",
18
19
  "VimbaDriver",
19
20
  "start_acquiring_driver_and_ensure_status",
21
+ "set_exposure_time_and_acquire_period_if_supplied",
20
22
  "DetectorState",
21
23
  ]
@@ -5,6 +5,7 @@ from typing import FrozenSet, Sequence, Set
5
5
  from ophyd_async.core import (
6
6
  DEFAULT_TIMEOUT,
7
7
  AsyncStatus,
8
+ DetectorControl,
8
9
  ShapeProvider,
9
10
  set_and_wait_for_value,
10
11
  )
@@ -44,6 +45,7 @@ class ADBase(NDArrayBase):
44
45
  def __init__(self, prefix: str, name: str = "") -> None:
45
46
  # Define some signals
46
47
  self.acquire_time = epics_signal_rw_rbv(float, prefix + "AcquireTime")
48
+ self.acquire_period = epics_signal_rw_rbv(float, prefix + "AcquirePeriod")
47
49
  self.num_images = epics_signal_rw_rbv(int, prefix + "NumImages")
48
50
  self.image_mode = epics_signal_rw_rbv(ImageMode, prefix + "ImageMode")
49
51
  self.detector_state = epics_signal_r(
@@ -52,6 +54,36 @@ class ADBase(NDArrayBase):
52
54
  super().__init__(prefix, name=name)
53
55
 
54
56
 
57
+ async def set_exposure_time_and_acquire_period_if_supplied(
58
+ controller: DetectorControl,
59
+ driver: ADBase,
60
+ exposure: float | None = None,
61
+ timeout: float = DEFAULT_TIMEOUT,
62
+ ) -> None:
63
+ """
64
+ Sets the exposure time if it is not None and the acquire period to the
65
+ exposure time plus the deadtime. This is expected behavior for most
66
+ AreaDetectors, but some may require more specialized handling.
67
+
68
+ Parameters
69
+ ----------
70
+ controller:
71
+ Controller that can supply a deadtime.
72
+ driver:
73
+ The driver to start acquiring. Must subclass ADBase.
74
+ exposure:
75
+ Desired exposure time, this is a noop if it is None.
76
+ timeout:
77
+ How long to wait for the exposure time and acquire period to be set.
78
+ """
79
+ if exposure is not None:
80
+ full_frame_time = exposure + controller.get_deadtime(exposure)
81
+ await asyncio.gather(
82
+ driver.acquire_time.set(exposure, timeout=timeout),
83
+ driver.acquire_period.set(full_frame_time, timeout=timeout),
84
+ )
85
+
86
+
55
87
  async def start_acquiring_driver_and_ensure_status(
56
88
  driver: ADBase,
57
89
  good_states: Set[DetectorState] = set(DEFAULT_GOOD_STATES),
@@ -1,6 +1,6 @@
1
1
  from enum import Enum
2
- from typing import Literal
3
2
 
3
+ from ophyd_async.core import SubsetEnum
4
4
  from ophyd_async.epics.areadetector.drivers import ADBase
5
5
  from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
6
6
 
@@ -19,7 +19,7 @@ class AravisTriggerMode(str, Enum):
19
19
  To prevent requiring one Enum class per possible configuration, we set as this Enum
20
20
  but read from the underlying signal as a str.
21
21
  """
22
- AravisTriggerSource = Literal["Freerun", "Line1", "Line2", "Line3", "Line4"]
22
+ AravisTriggerSource = SubsetEnum["Freerun", "Line1"]
23
23
 
24
24
 
25
25
  class AravisDriver(ADBase):
@@ -34,5 +34,7 @@ class AravisDriver(ADBase):
34
34
  self.trigger_mode = epics_signal_rw_rbv(
35
35
  AravisTriggerMode, prefix + "TriggerMode"
36
36
  )
37
- self.trigger_source = epics_signal_rw_rbv(str, prefix + "TriggerSource")
37
+ self.trigger_source = epics_signal_rw_rbv(
38
+ AravisTriggerSource, prefix + "TriggerSource"
39
+ )
38
40
  super().__init__(prefix, name=name)
@@ -95,5 +95,3 @@ class Motor(StandardReadable, Movable, Stoppable):
95
95
  # Put with completion will never complete as we are waiting for completion on
96
96
  # the move above, so need to pass wait=False
97
97
  await self.motor_stop.trigger(wait=False)
98
- # Trigger any callbacks
99
- await self.user_readback._backend.put(await self.user_readback.get_value())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ophyd-async
3
- Version: 0.3.2
3
+ Version: 0.3.4a1
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
@@ -156,6 +156,7 @@ tests/core/test_mock_signal_backend.py
156
156
  tests/core/test_signal.py
157
157
  tests/core/test_soft_signal_backend.py
158
158
  tests/core/test_standard_readable.py
159
+ tests/core/test_subset_enum.py
159
160
  tests/core/test_utils.py
160
161
  tests/core/test_watchable_async_status.py
161
162
  tests/epics/test_pvi.py
@@ -31,7 +31,7 @@ def string_d(value):
31
31
 
32
32
 
33
33
  def enum_d(value):
34
- return {"dtype": "string", "shape": [], "choices": ["Aaa", "Bbb", "Ccc"]}
34
+ return {"dtype": "string", "shape": [], "choices": ("Aaa", "Bbb", "Ccc")}
35
35
 
36
36
 
37
37
  def waveform_d(value):
@@ -0,0 +1,90 @@
1
+ import pytest
2
+ from epicscorelibs.ca import dbr
3
+ from p4p import Value as P4PValue
4
+ from p4p.nt import NTEnum
5
+
6
+ from ophyd_async.core import SubsetEnum
7
+ from ophyd_async.epics._backend._aioca import make_converter as aioca_make_converter
8
+ from ophyd_async.epics._backend._p4p import make_converter as p4p_make_converter
9
+ from ophyd_async.epics.signal.signal import epics_signal_rw
10
+
11
+
12
+ async def test_runtime_enum_behaviour():
13
+ rt_enum = SubsetEnum["A", "B"]
14
+
15
+ with pytest.raises(RuntimeError) as exc:
16
+ rt_enum()
17
+ assert str(exc.value) == "SubsetEnum cannot be instantiated"
18
+
19
+ assert issubclass(rt_enum, SubsetEnum)
20
+
21
+ # Our metaclass doesn't cache already created runtime enums,
22
+ # so we can't do this
23
+ assert not issubclass(rt_enum, SubsetEnum["A", "B"])
24
+ assert not issubclass(rt_enum, SubsetEnum["B", "A"])
25
+ assert rt_enum is not SubsetEnum["A", "B"]
26
+ assert rt_enum is not SubsetEnum["B", "A"]
27
+
28
+ assert str(rt_enum) == "SubsetEnum['A', 'B']"
29
+ assert str(SubsetEnum) == "SubsetEnum"
30
+
31
+ with pytest.raises(TypeError) as exc:
32
+ SubsetEnum["A", "B", "A"]
33
+ assert str(exc.value) == "Duplicate elements in runtime enum choices."
34
+
35
+
36
+ async def test_ca_runtime_enum_converter():
37
+ class EpicsValue:
38
+ def __init__(self):
39
+ self.name = "test"
40
+ self.ok = (True,)
41
+ self.errorcode = 0
42
+ self.datatype = dbr.DBR_ENUM
43
+ self.element_count = 1
44
+ self.severity = 0
45
+ self.status = 0
46
+ self.raw_stamp = (0,)
47
+ self.timestamp = 0
48
+ self.datetime = 0
49
+ self.enums = ["A", "B", "C"] # More than the runtime enum
50
+
51
+ epics_value = EpicsValue()
52
+ rt_enum = SubsetEnum["A", "B"]
53
+ converter = aioca_make_converter(
54
+ rt_enum, values={"READ_PV": epics_value, "WRITE_PV": epics_value}
55
+ )
56
+ assert converter.choices == {"A": "A", "B": "B", "C": "C"}
57
+ assert set(rt_enum.choices).issubset(set(converter.choices.keys()))
58
+
59
+
60
+ async def test_pva_runtime_enum_converter():
61
+ enum_type = NTEnum.buildType()
62
+ epics_value = P4PValue(
63
+ enum_type,
64
+ {
65
+ "value.choices": ["A", "B", "C"],
66
+ },
67
+ )
68
+ rt_enum = SubsetEnum["A", "B"]
69
+ converter = p4p_make_converter(
70
+ rt_enum, values={"READ_PV": epics_value, "WRITE_PV": epics_value}
71
+ )
72
+ assert {"A", "B"}.issubset(set(converter.choices))
73
+
74
+
75
+ async def test_runtime_enum_signal():
76
+ signal_rw_pva = epics_signal_rw(SubsetEnum["A1", "B1"], "ca://RW_PV", name="signal")
77
+ signal_rw_ca = epics_signal_rw(SubsetEnum["A2", "B2"], "ca://RW_PV", name="signal")
78
+ await signal_rw_pva.connect(mock=True)
79
+ await signal_rw_ca.connect(mock=True)
80
+ await signal_rw_pva.get_value() == "A1"
81
+ await signal_rw_ca.get_value() == "A2"
82
+ await signal_rw_pva.set("B1")
83
+ await signal_rw_ca.set("B2")
84
+ await signal_rw_pva.get_value() == "B1"
85
+ await signal_rw_ca.get_value() == "B2"
86
+
87
+ # Will accept string values even if they're not in the runtime enum
88
+ # Though type checking should compain
89
+ await signal_rw_pva.set("C1") # type: ignore
90
+ await signal_rw_ca.set("C2") # type: ignore
@@ -0,0 +1,96 @@
1
+ import asyncio
2
+ from unittest.mock import Mock
3
+
4
+ import pytest
5
+
6
+ from ophyd_async.core import (
7
+ DetectorControl,
8
+ DeviceCollector,
9
+ get_mock_put,
10
+ set_mock_value,
11
+ )
12
+ from ophyd_async.epics.areadetector.drivers import (
13
+ ADBase,
14
+ DetectorState,
15
+ set_exposure_time_and_acquire_period_if_supplied,
16
+ start_acquiring_driver_and_ensure_status,
17
+ )
18
+
19
+ TEST_DEADTIME = 0.1
20
+
21
+
22
+ @pytest.fixture
23
+ def driver(RE) -> ADBase:
24
+ with DeviceCollector(mock=True):
25
+ driver = ADBase("DRV:", name="drv")
26
+ return driver
27
+
28
+
29
+ @pytest.fixture
30
+ async def controller(RE, driver: ADBase) -> Mock:
31
+ controller = Mock(spec=DetectorControl)
32
+ controller.get_deadtime.return_value = TEST_DEADTIME
33
+ return controller
34
+
35
+
36
+ async def test_set_exposure_time_and_acquire_period_if_supplied_is_a_noop_if_no_exposure_supplied( # noqa: E501
37
+ controller: DetectorControl,
38
+ driver: ADBase,
39
+ ):
40
+ put_exposure = get_mock_put(driver.acquire_time)
41
+ put_acquire_period = get_mock_put(driver.acquire_period)
42
+ await set_exposure_time_and_acquire_period_if_supplied(controller, driver, None)
43
+ put_exposure.assert_not_called()
44
+ put_acquire_period.assert_not_called()
45
+
46
+
47
+ @pytest.mark.parametrize(
48
+ "exposure,expected_exposure,expected_acquire_period",
49
+ [
50
+ (0.0, 0.0, 0.1),
51
+ (1.0, 1.0, 1.1),
52
+ (1.5, 1.5, 1.6),
53
+ ],
54
+ )
55
+ async def test_set_exposure_time_and_acquire_period_if_supplied_uses_deadtime(
56
+ controller: DetectorControl,
57
+ driver: ADBase,
58
+ exposure: float,
59
+ expected_exposure: float,
60
+ expected_acquire_period: float,
61
+ ):
62
+ await set_exposure_time_and_acquire_period_if_supplied(controller, driver, exposure)
63
+ actual_exposure = await driver.acquire_time.get_value()
64
+ actual_acquire_period = await driver.acquire_period.get_value()
65
+ assert expected_exposure == actual_exposure
66
+ assert expected_acquire_period == actual_acquire_period
67
+
68
+
69
+ async def test_start_acquiring_driver_and_ensure_status_flags_immediate_failure(
70
+ driver: ADBase,
71
+ ):
72
+ set_mock_value(driver.detector_state, DetectorState.Error)
73
+ acquiring = await start_acquiring_driver_and_ensure_status(driver, timeout=0.01)
74
+ with pytest.raises(ValueError):
75
+ await acquiring
76
+
77
+
78
+ async def test_start_acquiring_driver_and_ensure_status_fails_after_some_time(
79
+ driver: ADBase,
80
+ ):
81
+ """This test ensures a failing status is captured halfway through acquisition.
82
+
83
+ Real world application; it takes some time to start acquiring, and during that time
84
+ the detector gets itself into a bad state.
85
+ """
86
+ set_mock_value(driver.detector_state, DetectorState.Idle)
87
+
88
+ async def wait_then_fail():
89
+ await asyncio.sleep(0)
90
+ set_mock_value(driver.detector_state, DetectorState.Disconnected)
91
+
92
+ acquiring = await start_acquiring_driver_and_ensure_status(driver, timeout=0.1)
93
+ await wait_then_fail()
94
+
95
+ with pytest.raises(ValueError):
96
+ await acquiring