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.
Files changed (201) hide show
  1. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/PKG-INFO +2 -2
  2. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/pyproject.toml +1 -1
  3. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/_version.py +2 -2
  4. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/signal.py +18 -6
  5. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/soft_signal_backend.py +23 -9
  6. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/_backend/_aioca.py +5 -3
  7. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/_common_blocks.py +2 -1
  8. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/_hdf_panda.py +0 -2
  9. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/_table.py +10 -0
  10. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/writers/_hdf_writer.py +22 -105
  11. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/writers/_panda_hdf_file.py +4 -8
  12. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async.egg-info/PKG-INFO +2 -2
  13. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async.egg-info/requires.txt +1 -1
  14. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_mock_signal_backend.py +32 -0
  15. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_soft_signal_backend.py +37 -1
  16. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/test_hdf_panda.py +22 -24
  17. ophyd_async-0.3.2/tests/panda/test_writer.py +198 -0
  18. ophyd_async-0.3.1/tests/panda/test_writer.py +0 -208
  19. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.codecov.yml +0 -0
  20. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.copier-answers.yml +0 -0
  21. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.devcontainer/devcontainer.json +0 -0
  22. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.git-blame-ignore-revs +0 -0
  23. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/CONTRIBUTING.md +0 -0
  24. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/actions/install_requirements/action.yml +0 -0
  25. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/dependabot.yml +0 -0
  26. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/pages/index.html +0 -0
  27. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/pages/make_switcher.py +0 -0
  28. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_check.yml +0 -0
  29. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_dist.yml +0 -0
  30. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_docs.yml +0 -0
  31. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_pypi.yml +0 -0
  32. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_release.yml +0 -0
  33. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_test.yml +0 -0
  34. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/_tox.yml +0 -0
  35. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/ci.yml +0 -0
  36. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.github/workflows/periodic.yml +0 -0
  37. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.gitignore +0 -0
  38. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.mailmap +0 -0
  39. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/.pre-commit-config.yaml +0 -0
  40. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/Dockerfile +0 -0
  41. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/LICENSE +0 -0
  42. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/README.md +0 -0
  43. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/_templates/README +0 -0
  44. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/_templates/custom-class-template.rst +0 -0
  45. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/_templates/custom-module-template.rst +0 -0
  46. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/conf.py +0 -0
  47. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/examples/epics_demo.py +0 -0
  48. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/examples/foo_detector.py +0 -0
  49. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/0001-record-architecture-decisions.md +0 -0
  50. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/0002-switched-to-python-copier-template.md +0 -0
  51. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/0003-ophyd-async-migration.rst +0 -0
  52. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/0004-repository-structure.rst +0 -0
  53. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/0005-respect-black-line-length.rst +0 -0
  54. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/0006-procedural-device-definitions.rst +0 -0
  55. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions/COPYME +0 -0
  56. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/decisions.md +0 -0
  57. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/design-goals.rst +0 -0
  58. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/event-loop-choice.rst +0 -0
  59. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations/flyscanning.rst +0 -0
  60. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/explanations.md +0 -0
  61. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/genindex.rst +0 -0
  62. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to/choose-interfaces-for-devices.md +0 -0
  63. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to/compound-devices.rst +0 -0
  64. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to/contribute.md +0 -0
  65. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to/make-a-simple-device.rst +0 -0
  66. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to/make-a-standard-detector.rst +0 -0
  67. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to/write-tests-for-devices.rst +0 -0
  68. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/how-to.md +0 -0
  69. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/images/bluesky_ophyd_epics_devices_logo.svg +0 -0
  70. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/images/bluesky_ophyd_logo.svg +0 -0
  71. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/images/ophyd_favicon.svg +0 -0
  72. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/index.md +0 -0
  73. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/reference/api.rst +0 -0
  74. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/reference.md +0 -0
  75. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/tutorials/installation.md +0 -0
  76. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/tutorials/using-existing-devices.rst +0 -0
  77. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/docs/tutorials.md +0 -0
  78. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/setup.cfg +0 -0
  79. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/__init__.py +0 -0
  80. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/__main__.py +0 -0
  81. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/__init__.py +0 -0
  82. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/_providers.py +0 -0
  83. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/async_status.py +0 -0
  84. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/detector.py +0 -0
  85. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/device.py +0 -0
  86. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/device_save_loader.py +0 -0
  87. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/flyer.py +0 -0
  88. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/mock_signal_backend.py +0 -0
  89. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/mock_signal_utils.py +0 -0
  90. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/signal_backend.py +0 -0
  91. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/standard_readable.py +0 -0
  92. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/core/utils.py +0 -0
  93. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/__init__.py +0 -0
  94. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/_backend/__init__.py +0 -0
  95. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/_backend/_p4p.py +0 -0
  96. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/_backend/common.py +0 -0
  97. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/__init__.py +0 -0
  98. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/aravis.py +0 -0
  99. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/controllers/__init__.py +0 -0
  100. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/controllers/ad_sim_controller.py +0 -0
  101. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/controllers/aravis_controller.py +0 -0
  102. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/controllers/kinetix_controller.py +0 -0
  103. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/controllers/pilatus_controller.py +0 -0
  104. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/controllers/vimba_controller.py +0 -0
  105. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/drivers/__init__.py +0 -0
  106. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/drivers/ad_base.py +0 -0
  107. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/drivers/aravis_driver.py +0 -0
  108. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/drivers/kinetix_driver.py +0 -0
  109. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/drivers/pilatus_driver.py +0 -0
  110. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/drivers/vimba_driver.py +0 -0
  111. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/kinetix.py +0 -0
  112. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/pilatus.py +0 -0
  113. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/single_trigger_det.py +0 -0
  114. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/utils.py +0 -0
  115. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/vimba.py +0 -0
  116. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/writers/__init__.py +0 -0
  117. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/writers/_hdfdataset.py +0 -0
  118. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/writers/_hdffile.py +0 -0
  119. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/writers/hdf_writer.py +0 -0
  120. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/writers/nd_file_hdf.py +0 -0
  121. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/areadetector/writers/nd_plugin.py +0 -0
  122. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/demo/__init__.py +0 -0
  123. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/demo/demo_ad_sim_detector.py +0 -0
  124. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/demo/mover.db +0 -0
  125. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/demo/sensor.db +0 -0
  126. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/motion/__init__.py +0 -0
  127. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/motion/motor.py +0 -0
  128. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/pvi/__init__.py +0 -0
  129. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/pvi/pvi.py +0 -0
  130. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/signal/__init__.py +0 -0
  131. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/signal/_epics_transport.py +0 -0
  132. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/epics/signal/signal.py +0 -0
  133. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/log.py +0 -0
  134. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/__init__.py +0 -0
  135. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/_panda_controller.py +0 -0
  136. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/_trigger.py +0 -0
  137. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/_utils.py +0 -0
  138. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/panda/writers/__init__.py +0 -0
  139. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/plan_stubs/__init__.py +0 -0
  140. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/plan_stubs/ensure_connected.py +0 -0
  141. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/plan_stubs/fly.py +0 -0
  142. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/protocols.py +0 -0
  143. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/__init__.py +0 -0
  144. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/demo/__init__.py +0 -0
  145. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/demo/sim_motor.py +0 -0
  146. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/pattern_generator.py +0 -0
  147. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/sim_pattern_detector_control.py +0 -0
  148. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/sim_pattern_detector_writer.py +0 -0
  149. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async/sim/sim_pattern_generator.py +0 -0
  150. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async.egg-info/SOURCES.txt +0 -0
  151. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async.egg-info/dependency_links.txt +0 -0
  152. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async.egg-info/entry_points.txt +0 -0
  153. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/src/ophyd_async.egg-info/top_level.txt +0 -0
  154. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/conftest.py +0 -0
  155. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_async_status.py +0 -0
  156. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_device.py +0 -0
  157. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_device_collector.py +0 -0
  158. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_device_save_loader.py +0 -0
  159. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_flyer.py +0 -0
  160. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_signal.py +0 -0
  161. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_standard_readable.py +0 -0
  162. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_utils.py +0 -0
  163. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/core/test_watchable_async_status.py +0 -0
  164. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/_backend/test_common.py +0 -0
  165. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/__init__.py +0 -0
  166. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_aravis.py +0 -0
  167. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_controllers.py +0 -0
  168. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_drivers.py +0 -0
  169. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_kinetix.py +0 -0
  170. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_pilatus.py +0 -0
  171. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_scans.py +0 -0
  172. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_single_trigger_det.py +0 -0
  173. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_utils.py +0 -0
  174. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_vimba.py +0 -0
  175. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/areadetector/test_writers.py +0 -0
  176. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/demo/test_demo.py +0 -0
  177. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/demo/test_demo_ad_sim_detector.py +0 -0
  178. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/motion/__init__.py +0 -0
  179. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/motion/test_motor.py +0 -0
  180. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/test_pvi.py +0 -0
  181. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/test_records.db +0 -0
  182. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/epics/test_signals.py +0 -0
  183. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/db/panda.db +0 -0
  184. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/test_panda_connect.py +0 -0
  185. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/test_panda_controller.py +0 -0
  186. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/test_panda_utils.py +0 -0
  187. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/test_table.py +0 -0
  188. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/panda/test_trigger.py +0 -0
  189. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/plan_stubs/test_fly.py +0 -0
  190. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/protocols/test_protocols.py +0 -0
  191. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/__init__.py +0 -0
  192. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/conftest.py +0 -0
  193. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/demo/__init__.py +0 -0
  194. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/demo/test_sim_motor.py +0 -0
  195. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/test_pattern_generator.py +0 -0
  196. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/test_sim_detector.py +0 -0
  197. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/test_sim_writer.py +0 -0
  198. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/sim/test_streaming_plan.py +0 -0
  199. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/test_cli.py +0 -0
  200. {ophyd_async-0.3.1 → ophyd_async-0.3.2}/tests/test_data/test_yaml_save.yml +0 -0
  201. {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.1
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
@@ -13,7 +13,7 @@ classifiers = [
13
13
  description = "Asynchronous Bluesky hardware abstraction code, compatible with control systems like EPICS and Tango"
14
14
  dependencies = [
15
15
  "networkx>=2.0",
16
- "numpy",
16
+ "numpy<2.0.0",
17
17
  "packaging",
18
18
  "pint",
19
19
  "bluesky>=1.13.0a3",
@@ -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.1'
16
- __version_tuple__ = version_tuple = (0, 3, 1)
15
+ __version__ = version = '0.3.2'
16
+ __version_tuple__ = version_tuple = (0, 3, 2)
@@ -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
- signal = SignalRW(SoftSignalBackend(datatype, initial_value), name=name)
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. Use
288
- soft_signal_rw if you want a device that is externally modifiable
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
- backend = SoftSignalBackend(datatype, initial_value)
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
- dtype_name = primitive_dtypes[dtype]
48
- return {"source": source, "dtype": dtype_name, "shape": []}
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 {"source": source, "dtype": "string", "shape": [], "choices": choices} # type: ignore
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", None)
92
- high = getattr(value, f"upper_{limit}_limit", None)
93
- return LimitPair(low=low, high=high)
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 Any, AsyncGenerator, AsyncIterator, Dict, List, Optional
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
- for suffix in capture_signal.capture_type.split(" "):
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.name: DataKey(
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
- device_name: str
13
- block: str
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.name,
30
+ data_key=ds.data_key,
33
31
  resource_path=(f"{str(directory_info.root)}/{full_file_name}"),
34
32
  resource_kwargs={
35
- "name": ds.name,
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.1
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
@@ -1,5 +1,5 @@
1
1
  networkx>=2.0
2
- numpy
2
+ numpy<2.0.0
3
3
  packaging
4
4
  pint
5
5
  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.writers._hdf_writer import Capture
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
- set_mock_value(block_a.test_capture, Capture.Min)
47
- set_mock_value(block_b.test_capture, Capture.Diff)
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 block_letter, stream_resource, data_key_name in zip(
142
- ("a", "b"), docs["stream_resource"], data_key_names
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
- "block": f"block_{block_letter}",
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