ophyd-async 0.1.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

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 (94) hide show
  1. ophyd_async/__init__.py +1 -4
  2. ophyd_async/_version.py +2 -2
  3. ophyd_async/core/__init__.py +91 -19
  4. ophyd_async/core/_providers.py +68 -0
  5. ophyd_async/core/async_status.py +90 -42
  6. ophyd_async/core/detector.py +341 -0
  7. ophyd_async/core/device.py +226 -0
  8. ophyd_async/core/device_save_loader.py +286 -0
  9. ophyd_async/core/flyer.py +85 -0
  10. ophyd_async/core/mock_signal_backend.py +82 -0
  11. ophyd_async/core/mock_signal_utils.py +145 -0
  12. ophyd_async/core/{_device/_signal/signal.py → signal.py} +249 -61
  13. ophyd_async/core/{_device/_backend/signal_backend.py → signal_backend.py} +12 -5
  14. ophyd_async/core/{_device/_backend/sim_signal_backend.py → soft_signal_backend.py} +54 -48
  15. ophyd_async/core/standard_readable.py +261 -0
  16. ophyd_async/core/utils.py +127 -30
  17. ophyd_async/epics/_backend/_aioca.py +62 -43
  18. ophyd_async/epics/_backend/_p4p.py +100 -52
  19. ophyd_async/epics/_backend/common.py +25 -0
  20. ophyd_async/epics/areadetector/__init__.py +16 -15
  21. ophyd_async/epics/areadetector/aravis.py +63 -0
  22. ophyd_async/epics/areadetector/controllers/__init__.py +5 -0
  23. ophyd_async/epics/areadetector/controllers/ad_sim_controller.py +52 -0
  24. ophyd_async/epics/areadetector/controllers/aravis_controller.py +78 -0
  25. ophyd_async/epics/areadetector/controllers/kinetix_controller.py +49 -0
  26. ophyd_async/epics/areadetector/controllers/pilatus_controller.py +61 -0
  27. ophyd_async/epics/areadetector/controllers/vimba_controller.py +66 -0
  28. ophyd_async/epics/areadetector/drivers/__init__.py +21 -0
  29. ophyd_async/epics/areadetector/drivers/ad_base.py +107 -0
  30. ophyd_async/epics/areadetector/drivers/aravis_driver.py +38 -0
  31. ophyd_async/epics/areadetector/drivers/kinetix_driver.py +27 -0
  32. ophyd_async/epics/areadetector/drivers/pilatus_driver.py +21 -0
  33. ophyd_async/epics/areadetector/drivers/vimba_driver.py +63 -0
  34. ophyd_async/epics/areadetector/kinetix.py +46 -0
  35. ophyd_async/epics/areadetector/pilatus.py +45 -0
  36. ophyd_async/epics/areadetector/single_trigger_det.py +18 -10
  37. ophyd_async/epics/areadetector/utils.py +91 -13
  38. ophyd_async/epics/areadetector/vimba.py +43 -0
  39. ophyd_async/epics/areadetector/writers/__init__.py +5 -0
  40. ophyd_async/epics/areadetector/writers/_hdfdataset.py +10 -0
  41. ophyd_async/epics/areadetector/writers/_hdffile.py +54 -0
  42. ophyd_async/epics/areadetector/writers/hdf_writer.py +142 -0
  43. ophyd_async/epics/areadetector/writers/nd_file_hdf.py +40 -0
  44. ophyd_async/epics/areadetector/writers/nd_plugin.py +38 -0
  45. ophyd_async/epics/demo/__init__.py +78 -51
  46. ophyd_async/epics/demo/demo_ad_sim_detector.py +35 -0
  47. ophyd_async/epics/motion/motor.py +67 -52
  48. ophyd_async/epics/pvi/__init__.py +3 -0
  49. ophyd_async/epics/pvi/pvi.py +318 -0
  50. ophyd_async/epics/signal/__init__.py +8 -3
  51. ophyd_async/epics/signal/signal.py +27 -10
  52. ophyd_async/log.py +130 -0
  53. ophyd_async/panda/__init__.py +24 -7
  54. ophyd_async/panda/_common_blocks.py +49 -0
  55. ophyd_async/panda/_hdf_panda.py +48 -0
  56. ophyd_async/panda/_panda_controller.py +37 -0
  57. ophyd_async/panda/_table.py +158 -0
  58. ophyd_async/panda/_trigger.py +39 -0
  59. ophyd_async/panda/_utils.py +15 -0
  60. ophyd_async/panda/writers/__init__.py +3 -0
  61. ophyd_async/panda/writers/_hdf_writer.py +220 -0
  62. ophyd_async/panda/writers/_panda_hdf_file.py +58 -0
  63. ophyd_async/plan_stubs/__init__.py +13 -0
  64. ophyd_async/plan_stubs/ensure_connected.py +22 -0
  65. ophyd_async/plan_stubs/fly.py +149 -0
  66. ophyd_async/protocols.py +126 -0
  67. ophyd_async/sim/__init__.py +11 -0
  68. ophyd_async/sim/demo/__init__.py +3 -0
  69. ophyd_async/sim/demo/sim_motor.py +103 -0
  70. ophyd_async/sim/pattern_generator.py +318 -0
  71. ophyd_async/sim/sim_pattern_detector_control.py +55 -0
  72. ophyd_async/sim/sim_pattern_detector_writer.py +34 -0
  73. ophyd_async/sim/sim_pattern_generator.py +37 -0
  74. {ophyd_async-0.1.0.dist-info → ophyd_async-0.3.0.dist-info}/METADATA +35 -67
  75. ophyd_async-0.3.0.dist-info/RECORD +86 -0
  76. {ophyd_async-0.1.0.dist-info → ophyd_async-0.3.0.dist-info}/WHEEL +1 -1
  77. ophyd_async/core/_device/__init__.py +0 -0
  78. ophyd_async/core/_device/_backend/__init__.py +0 -0
  79. ophyd_async/core/_device/_signal/__init__.py +0 -0
  80. ophyd_async/core/_device/device.py +0 -60
  81. ophyd_async/core/_device/device_collector.py +0 -121
  82. ophyd_async/core/_device/device_vector.py +0 -14
  83. ophyd_async/core/_device/standard_readable.py +0 -72
  84. ophyd_async/epics/areadetector/ad_driver.py +0 -18
  85. ophyd_async/epics/areadetector/directory_provider.py +0 -18
  86. ophyd_async/epics/areadetector/hdf_streamer_det.py +0 -167
  87. ophyd_async/epics/areadetector/nd_file_hdf.py +0 -22
  88. ophyd_async/epics/areadetector/nd_plugin.py +0 -13
  89. ophyd_async/epics/signal/pvi_get.py +0 -22
  90. ophyd_async/panda/panda.py +0 -332
  91. ophyd_async-0.1.0.dist-info/RECORD +0 -45
  92. {ophyd_async-0.1.0.dist-info → ophyd_async-0.3.0.dist-info}/LICENSE +0 -0
  93. {ophyd_async-0.1.0.dist-info → ophyd_async-0.3.0.dist-info}/entry_points.txt +0 -0
  94. {ophyd_async-0.1.0.dist-info → ophyd_async-0.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,86 @@
1
+ ophyd_async/__init__.py,sha256=v-rRiDOgZ3sQSMQKq0vgUQZvpeOkoHFXissAx6Ktg84,61
2
+ ophyd_async/__main__.py,sha256=G-Zcv_G9zK7Nhx6o5L5w-wyhMxdl_WgyMELu8IMFqAE,328
3
+ ophyd_async/_version.py,sha256=Jk2iAU7m-7Vx9XV1TtdD9ZoJraIncDq_4_Wd-qtUotg,411
4
+ ophyd_async/log.py,sha256=DbMjt0bkfUOLHIinZYt0Q0FHZmCXXi5x8y0uFiEmqoQ,3587
5
+ ophyd_async/protocols.py,sha256=EF2W9nfElV-0QNMYrX1zusL1PqDJR3kNsjlalR29j0I,3412
6
+ ophyd_async/core/__init__.py,sha256=znjVeRfrDVJbGLEkUczeKMW46kV6HDrlE4lV0SqvZt4,2952
7
+ ophyd_async/core/_providers.py,sha256=LrlTMPHKXWOPVkpAOw-pqBq0kip-c3C9ZZPoFfiaV4M,2212
8
+ ophyd_async/core/async_status.py,sha256=9TOgOXIAuH62RDo5t-Y5GdjrJ76d_6TFlBxYv-5_a88,4367
9
+ ophyd_async/core/detector.py,sha256=8mdLKphirgit5CVCklJI9eHqKKiCz4CYs9BElo10-lc,11007
10
+ ophyd_async/core/device.py,sha256=280zFnLCoiMZAA-Dh1_AjUSnhxUfKYGgj4H_2S1njOA,7086
11
+ ophyd_async/core/device_save_loader.py,sha256=RXA3dPUPihAR2ZGDStlGiA-TAsr_xqL0snsCjMsMnfA,9138
12
+ ophyd_async/core/flyer.py,sha256=bIjzBkrl8HVAlKgsZ_FF0WL69Qvksyzp9ZWmTLl8Yrw,2304
13
+ ophyd_async/core/mock_signal_backend.py,sha256=Ug6jK72wm9vM6EueoUrYgcXtiFzdPUEISRe86LdyYKc,2844
14
+ ophyd_async/core/mock_signal_utils.py,sha256=bF8MVZA1j9zCmS2tBbgUdfwNmcHniHixnmotjd0g7hs,4083
15
+ ophyd_async/core/signal.py,sha256=FbTb5qDPLhVxEbh6gimqXfkZwcqB4ymHTEYVXZVZYrk,16456
16
+ ophyd_async/core/signal_backend.py,sha256=fT3q0WED3JHmNKYCs7PzDLCK4cUPVin3wQjDNPdHqAY,1525
17
+ ophyd_async/core/soft_signal_backend.py,sha256=56zvcEi4c8n1yYbafTbp7X0VhSkhoehm3L8RBhu2fik,5596
18
+ ophyd_async/core/standard_readable.py,sha256=uVG3vs3s7-Kzg5dRCtT4I2mhZPqwVGYy2dxNmaOpDVU,8980
19
+ ophyd_async/core/utils.py,sha256=3oZcXNqAUHX4ZWMBH5gSuK6cFWEhSkZ9GSDYv0pf8jc,5783
20
+ ophyd_async/epics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ ophyd_async/epics/_backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ ophyd_async/epics/_backend/_aioca.py,sha256=cpPNZmRMi7FnAh2-3ec5uklLVFOqsmEmpI1nh5Ud1Ls,8794
23
+ ophyd_async/epics/_backend/_p4p.py,sha256=lIKx7kQ2o8h3M4wPwrq8JBo8xuDnasJxpDH2ATVBY78,12257
24
+ ophyd_async/epics/_backend/common.py,sha256=16mAuxDwA3eZFjUW8DHMabaW3CtEI0Qe8DLpP2xlW7Y,814
25
+ ophyd_async/epics/areadetector/__init__.py,sha256=ViKzx-wUxkRyNR33wfpL11QB97o0J47_KMyI2C_NphI,510
26
+ ophyd_async/epics/areadetector/aravis.py,sha256=YklN4V0loqUQBs4swVX304N49JIGPvrNOk8iA5EWofg,2127
27
+ ophyd_async/epics/areadetector/kinetix.py,sha256=7rE2MLnz9DEmeiN9pCekDfpXuZ2DErnMajRp_9eoLZY,1359
28
+ ophyd_async/epics/areadetector/pilatus.py,sha256=ki-BOBCEIiUD2wAtmujBIB1eX-nbXB4yMLJK_Q3opRM,1398
29
+ ophyd_async/epics/areadetector/single_trigger_det.py,sha256=U92dqhioIfnve3jtCThq9gXBCdEzzqzY4ezk6rZV19g,1182
30
+ ophyd_async/epics/areadetector/utils.py,sha256=p66UbVdKRFj6Sm1Qvm23kmlVyBMMqIvXFxA3x17YnSk,2824
31
+ ophyd_async/epics/areadetector/vimba.py,sha256=IxG8KLzfb84iLtzf6ZoX9JikqZLP49lwkWu33bkDV9Y,1291
32
+ ophyd_async/epics/areadetector/controllers/__init__.py,sha256=af58ci7X2z2s_FyUwR3IGQrws8q4TKcBw7vFyIS5FoI,217
33
+ ophyd_async/epics/areadetector/controllers/ad_sim_controller.py,sha256=mthZ6WxajMEgUKptq3bnkIctbLhjzTagV66i1auB8cg,1587
34
+ ophyd_async/epics/areadetector/controllers/aravis_controller.py,sha256=CIfnZdq_NobO_UMC2TJoAfUEP9GlzZg5z5bz6Dn1DxY,2669
35
+ ophyd_async/epics/areadetector/controllers/kinetix_controller.py,sha256=9QmydX85QOXfQL_UX49M9EQ2b2hUZPVzLxgGQn-A9Oc,1611
36
+ ophyd_async/epics/areadetector/controllers/pilatus_controller.py,sha256=cd1CKkaXlwkpQ0I1VL7nN0U8R4VweTsa08WhvHYI4nY,2243
37
+ ophyd_async/epics/areadetector/controllers/vimba_controller.py,sha256=Eh4Hr9rWgq1mKvE93JzgixntjPHxF3_07GTFqiOdZqE,2123
38
+ ophyd_async/epics/areadetector/drivers/__init__.py,sha256=-Ib0Lz4fFQQmB7K0uFxMDvAerkLxadMQERH7lNAvrs4,495
39
+ ophyd_async/epics/areadetector/drivers/ad_base.py,sha256=18WFAiWEUg0H2LcvTQHrKYj2wThGafQzDpiyAWki6vo,3411
40
+ ophyd_async/epics/areadetector/drivers/aravis_driver.py,sha256=PmIygsVNoxxYHvZZzFAbAm2DXmXFc13nAzL_DJB6YSU,1464
41
+ ophyd_async/epics/areadetector/drivers/kinetix_driver.py,sha256=yIV23BkGBJ4i0VskLiLL7AFbadCCR6Ch1UwUDJ9r2YM,743
42
+ ophyd_async/epics/areadetector/drivers/pilatus_driver.py,sha256=0DsUu9vAPXDa2v8_V0f_kPjBtLu3y4_EkmFfFjYO4Gk,553
43
+ ophyd_async/epics/areadetector/drivers/vimba_driver.py,sha256=J54VtWkOklfbSqZYxGWH1e6Uzm9_Gph_ZbCf9Zax0LU,1713
44
+ ophyd_async/epics/areadetector/writers/__init__.py,sha256=tpPcrYd1hs8WS7C0gmCnR2EBwjE5RzCljI7WwZ2V_LM,191
45
+ ophyd_async/epics/areadetector/writers/_hdfdataset.py,sha256=E0C9VgsPyY35h7k0mvcIhjsIVNavApLxizqNWlM388w,167
46
+ ophyd_async/epics/areadetector/writers/_hdffile.py,sha256=YtUgOKX53m0TaFEGBW671qXqNuuEKxEyLV5Ein1fjvo,1799
47
+ ophyd_async/epics/areadetector/writers/hdf_writer.py,sha256=ZpbVilNVv81OpbCrqaeZUoHLarrjzRWEGe-zI1Wxyyw,5436
48
+ ophyd_async/epics/areadetector/writers/nd_file_hdf.py,sha256=EkiaEh_0U6Iz17jFi2IIsRPsVQTQIJRG8EPNCiAHkkU,1762
49
+ ophyd_async/epics/areadetector/writers/nd_plugin.py,sha256=GUzaeTMdG07Rb1x0WzBBxMEltBhr10jb5dL29tEWXEQ,1547
50
+ ophyd_async/epics/demo/__init__.py,sha256=ZcuZ66aIQ58WSydLOSKnk-h_W-aWjhDRZkWQA6f3sig,5790
51
+ ophyd_async/epics/demo/demo_ad_sim_detector.py,sha256=06y65yvaqXvL2rDocjYyLz9kTVzuwV-LeuPhEfExdOA,944
52
+ ophyd_async/epics/demo/mover.db,sha256=RFz0rxZue689Wh1sWTZwWeFMUrH04ttPq2u5xJH_Fp4,998
53
+ ophyd_async/epics/demo/sensor.db,sha256=AVtiydrdtwAz2EFurO2Ult9SSRtre3r0akOBbL98LT0,554
54
+ ophyd_async/epics/motion/__init__.py,sha256=tnmVRIwKa9PdN_xonJdAUD04UpEceh-hoD7XI62yDB0,46
55
+ ophyd_async/epics/motion/motor.py,sha256=G8cc-okSXJ6s2fGxRO155xm7PrBbVImBmBMRWts895k,3630
56
+ ophyd_async/epics/pvi/__init__.py,sha256=TbOQNY4enQWgtr1T7x129vpo2p7FIFlr8cyZqqv5Lk4,158
57
+ ophyd_async/epics/pvi/pvi.py,sha256=PJdY3rCRyIQbsbHDru-TJ-IVOItyaQwCQKAC0Widu6A,11363
58
+ ophyd_async/epics/signal/__init__.py,sha256=JXKBSGpRL9y3auh27JRxsqDn_rBOXpJjtd4nCuDOX2g,261
59
+ ophyd_async/epics/signal/_epics_transport.py,sha256=DEIL0iYUAWssysVEgWGu1fHSM1l-ATV2kjUgPtDN9LY,858
60
+ ophyd_async/epics/signal/signal.py,sha256=M8ZVG_zLdYJfroCRX-u_w8c3yIhswSRw8e3RkW2szio,3166
61
+ ophyd_async/panda/__init__.py,sha256=FuSnvp-RtdA0X4RcHEF0nTiXymRts2MNdFmF_1_i41w,775
62
+ ophyd_async/panda/_common_blocks.py,sha256=n0PPc1rar43oDSIA-yNubTc8fR5YCW1tyjQU58whsg0,1038
63
+ ophyd_async/panda/_hdf_panda.py,sha256=QjfZyYos0ZBlIqBiZ5UbyEd_wuh_cGzwV8QE9jvLiIY,1419
64
+ ophyd_async/panda/_panda_controller.py,sha256=dIqcjmaIHVrki8UXSoDx46kk6I2Lhpe2o3sXNg5f-RQ,1238
65
+ ophyd_async/panda/_table.py,sha256=dLoRP4zYNOkD_s0Vkp2wVYAwkjVG8nNdf8-FaXOTfPo,5655
66
+ ophyd_async/panda/_trigger.py,sha256=tBH8uq_4o1ASG9yofVxq3tjf5v8LPzniDTRL4yjramI,1195
67
+ ophyd_async/panda/_utils.py,sha256=VHW5kPVISyEkmse_qQcyisBkkEwMO6GG2Ago-CH1AFA,487
68
+ ophyd_async/panda/writers/__init__.py,sha256=xy7BguVQG4HNIDBfKPjMj0KQo1tptC9LbCpEuMcVGaM,70
69
+ ophyd_async/panda/writers/_hdf_writer.py,sha256=vnyIg3JmlzMIIq75o0IDMfGzBm_GJAhOUisAZE_0cyg,7597
70
+ ophyd_async/panda/writers/_panda_hdf_file.py,sha256=42iHaTax4JjOBpNC7d4nkNL9SM14OTnFPTIcXv2jg-4,1759
71
+ ophyd_async/plan_stubs/__init__.py,sha256=nO9ELG9J7fYwfVTVRWVorz4kffeszYpwk1ROh6Ha--w,405
72
+ ophyd_async/plan_stubs/ensure_connected.py,sha256=1MkDu8UqVRPHLnW9IXRn-QvKiG8-rCV8T4KDbjf9K6w,557
73
+ ophyd_async/plan_stubs/fly.py,sha256=nl8XLoY7hvlam6H3zl4NcPRUiEJ3xIjopHEfA0ehTDg,4845
74
+ ophyd_async/sim/__init__.py,sha256=ScjH1g7FMo5yPACfJRZE6xGBWCHU4bKDzNQk1tqObnA,366
75
+ ophyd_async/sim/pattern_generator.py,sha256=pvSk2zb82D08j2jiKAMqMAfRohGnYd_rpjUraLrCD6c,10640
76
+ ophyd_async/sim/sim_pattern_detector_control.py,sha256=Ypz8IuRYAY2J243IhVbNyGr_Z-XtpJZ1qxma6NR3TgM,1838
77
+ ophyd_async/sim/sim_pattern_detector_writer.py,sha256=ESpcVyHd1TP7Cojznv2hJAwLinu3XbgAiVKfX12FCII,1237
78
+ ophyd_async/sim/sim_pattern_generator.py,sha256=fbcwWxTPYKLK33OzIY15vGylnonOO8HIudz1y_56GZU,1336
79
+ ophyd_async/sim/demo/__init__.py,sha256=9mxKpslrL89cfSj4g3og8Br3O--pMj3hhWZS-Xu6kyA,56
80
+ ophyd_async/sim/demo/sim_motor.py,sha256=a2p5wnHXjF-V5zOFai7jnszk4kbGmrZRnUqBtkOgEfQ,3733
81
+ ophyd_async-0.3.0.dist-info/LICENSE,sha256=pU5shZcsvWgz701EbT7yjFZ8rMvZcWgRH54CRt8ld_c,1517
82
+ ophyd_async-0.3.0.dist-info/METADATA,sha256=aNVgEtFpaffvpH6YavrgqWN48m0HO0m38POJ17FPjGM,6285
83
+ ophyd_async-0.3.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
84
+ ophyd_async-0.3.0.dist-info/entry_points.txt,sha256=O0YNJTEufO0w9BozXi-JurTy2U1_o0ypeCgJLQ727Jk,58
85
+ ophyd_async-0.3.0.dist-info/top_level.txt,sha256=-hjorMsv5Rmjo3qrgqhjpal1N6kW5vMxZO3lD4iEaXs,12
86
+ ophyd_async-0.3.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
File without changes
File without changes
File without changes
@@ -1,60 +0,0 @@
1
- """Base device"""
2
- from __future__ import annotations
3
-
4
- from typing import Iterator, Optional, Tuple
5
-
6
- from bluesky.protocols import HasName
7
-
8
- from ..utils import wait_for_connection
9
-
10
-
11
- class Device(HasName):
12
- """Common base class for all Ophyd Async Devices.
13
-
14
- By default, names and connects all Device children.
15
- """
16
-
17
- _name: str = ""
18
- #: The parent Device if it exists
19
- parent: Optional[Device] = None
20
-
21
- def __init__(self, name: str = "") -> None:
22
- self.set_name(name)
23
-
24
- @property
25
- def name(self) -> str:
26
- """Return the name of the Device"""
27
- return self._name
28
-
29
- def children(self) -> Iterator[Tuple[str, Device]]:
30
- for attr_name, attr in self.__dict__.items():
31
- if attr_name != "parent" and isinstance(attr, Device):
32
- yield attr_name, attr
33
-
34
- def set_name(self, name: str):
35
- """Set ``self.name=name`` and each ``self.child.name=name+"-child"``.
36
-
37
- Parameters
38
- ----------
39
- name:
40
- New name to set
41
- """
42
- self._name = name
43
- for attr_name, child in self.children():
44
- child_name = f"{name}-{attr_name.rstrip('_')}" if name else ""
45
- child.set_name(child_name)
46
- child.parent = self
47
-
48
- async def connect(self, sim: bool = False):
49
- """Connect self and all child Devices.
50
-
51
- Parameters
52
- ----------
53
- sim:
54
- If True then connect in simulation mode.
55
- """
56
- coros = {
57
- name: child_device.connect(sim) for name, child_device in self.children()
58
- }
59
- if coros:
60
- await wait_for_connection(**coros)
@@ -1,121 +0,0 @@
1
- """Interface for connecting and naming multiple devices"""
2
- import asyncio
3
- import logging
4
- import sys
5
- from contextlib import suppress
6
- from typing import Any, Dict, Set
7
-
8
- from bluesky.run_engine import call_in_bluesky_event_loop
9
-
10
- from ..utils import NotConnected
11
- from .device import Device
12
-
13
-
14
- class DeviceCollector:
15
- """Collector of top level Device instances to be used as a context manager
16
-
17
- Parameters
18
- ----------
19
- set_name:
20
- If True, call ``device.set_name(variable_name)`` on all collected
21
- Devices
22
- connect:
23
- If True, call ``device.connect(sim)`` in parallel on all
24
- collected Devices
25
- sim:
26
- If True, connect Signals in simulation mode
27
- timeout:
28
- How long to wait for connect before logging an exception
29
-
30
- Notes
31
- -----
32
- Example usage::
33
-
34
- [async] with DeviceCollector():
35
- t1x = motor.Motor("BLxxI-MO-TABLE-01:X")
36
- t1y = motor.Motor("pva://BLxxI-MO-TABLE-01:Y")
37
- # Names and connects devices here
38
- assert t1x.comm.velocity.source
39
- assert t1x.name == "t1x"
40
-
41
- """
42
-
43
- def __init__(
44
- self,
45
- set_name=True,
46
- connect=True,
47
- sim=False,
48
- timeout: float = 10.0,
49
- ):
50
- self._set_name = set_name
51
- self._connect = connect
52
- self._sim = sim
53
- self._timeout = timeout
54
- self._names_on_enter: Set[str] = set()
55
- self._objects_on_exit: Dict[str, Any] = {}
56
-
57
- def _caller_locals(self):
58
- """Walk up until we find a stack frame that doesn't have us as self"""
59
- try:
60
- raise ValueError
61
- except ValueError:
62
- _, _, tb = sys.exc_info()
63
- assert tb, "Can't get traceback, this shouldn't happen"
64
- caller_frame = tb.tb_frame
65
- while caller_frame.f_locals.get("self", None) is self:
66
- caller_frame = caller_frame.f_back
67
- return caller_frame.f_locals
68
-
69
- def __enter__(self) -> "DeviceCollector":
70
- # Stash the names that were defined before we were called
71
- self._names_on_enter = set(self._caller_locals())
72
- return self
73
-
74
- async def __aenter__(self) -> "DeviceCollector":
75
- return self.__enter__()
76
-
77
- async def _on_exit(self) -> None:
78
- # Name and kick off connect for devices
79
- tasks: Dict[asyncio.Task, str] = {}
80
- for name, obj in self._objects_on_exit.items():
81
- if name not in self._names_on_enter and isinstance(obj, Device):
82
- if self._set_name and not obj.name:
83
- obj.set_name(name)
84
- if self._connect:
85
- task = asyncio.create_task(obj.connect(self._sim))
86
- tasks[task] = name
87
- # Wait for all the signals to have finished
88
- if tasks:
89
- await self._wait_for_tasks(tasks)
90
-
91
- async def _wait_for_tasks(self, tasks: Dict[asyncio.Task, str]):
92
- done, pending = await asyncio.wait(tasks, timeout=self._timeout)
93
- if pending:
94
- msg = f"{len(pending)} Devices did not connect:"
95
- for t in pending:
96
- t.cancel()
97
- with suppress(Exception):
98
- await t
99
- e = t.exception()
100
- msg += f"\n {tasks[t]}: {type(e).__name__}"
101
- lines = str(e).splitlines()
102
- if len(lines) <= 1:
103
- msg += f": {e}"
104
- else:
105
- msg += "".join(f"\n {line}" for line in lines)
106
- logging.error(msg)
107
- raised = [t for t in done if t.exception()]
108
- if raised:
109
- logging.error(f"{len(raised)} Devices raised an error:")
110
- for t in raised:
111
- logging.exception(f" {tasks[t]}:", exc_info=t.exception())
112
- if pending or raised:
113
- raise NotConnected("Not all Devices connected")
114
-
115
- async def __aexit__(self, type, value, traceback):
116
- self._objects_on_exit = self._caller_locals()
117
- await self._on_exit()
118
-
119
- def __exit__(self, type_, value, traceback):
120
- self._objects_on_exit = self._caller_locals()
121
- return call_in_bluesky_event_loop(self._on_exit())
@@ -1,14 +0,0 @@
1
- """Dictionary which can contain mappings between integers and devices."""
2
-
3
- from typing import Dict, Generator, Tuple, TypeVar
4
-
5
- from .device import Device
6
-
7
- VT = TypeVar("VT", bound=Device)
8
-
9
-
10
- class DeviceVector(Dict[int, VT], Device):
11
- def children(self) -> Generator[Tuple[str, Device], None, None]:
12
- for attr_name, attr in self.items():
13
- if isinstance(attr, Device):
14
- yield str(attr_name), attr
@@ -1,72 +0,0 @@
1
- from typing import Dict, Sequence, Tuple
2
-
3
- from bluesky.protocols import Configurable, Descriptor, Readable, Reading, Stageable
4
-
5
- from ..async_status import AsyncStatus
6
- from ..utils import merge_gathered_dicts
7
- from ._signal.signal import SignalR
8
- from .device import Device
9
-
10
-
11
- class StandardReadable(Device, Readable, Configurable, Stageable):
12
- """Device that owns its children and provides useful default behavior.
13
-
14
- - When its name is set it renames child Devices
15
- - Signals can be registered for read() and read_configuration()
16
- - These signals will be subscribed for read() between stage() and unstage()
17
- """
18
-
19
- _read_signals: Tuple[SignalR, ...] = ()
20
- _configuration_signals: Tuple[SignalR, ...] = ()
21
- _read_uncached_signals: Tuple[SignalR, ...] = ()
22
-
23
- def set_readable_signals(
24
- self,
25
- read: Sequence[SignalR] = (),
26
- config: Sequence[SignalR] = (),
27
- read_uncached: Sequence[SignalR] = (),
28
- ):
29
- """
30
- Parameters
31
- ----------
32
- read:
33
- Signals to make up :meth:`~StandardReadable.read`
34
- conf:
35
- Signals to make up :meth:`~StandardReadable.read_configuration`
36
- read_uncached:
37
- Signals to make up :meth:`~StandardReadable.read` that won't be cached
38
- """
39
- self._read_signals = tuple(read)
40
- self._configuration_signals = tuple(config)
41
- self._read_uncached_signals = tuple(read_uncached)
42
-
43
- @AsyncStatus.wrap
44
- async def stage(self) -> None:
45
- for sig in self._read_signals + self._configuration_signals:
46
- await sig.stage().task
47
-
48
- @AsyncStatus.wrap
49
- async def unstage(self) -> None:
50
- for sig in self._read_signals + self._configuration_signals:
51
- await sig.unstage().task
52
-
53
- async def describe_configuration(self) -> Dict[str, Descriptor]:
54
- return await merge_gathered_dicts(
55
- [sig.describe() for sig in self._configuration_signals]
56
- )
57
-
58
- async def read_configuration(self) -> Dict[str, Reading]:
59
- return await merge_gathered_dicts(
60
- [sig.read() for sig in self._configuration_signals]
61
- )
62
-
63
- async def describe(self) -> Dict[str, Descriptor]:
64
- return await merge_gathered_dicts(
65
- [sig.describe() for sig in self._read_signals + self._read_uncached_signals]
66
- )
67
-
68
- async def read(self) -> Dict[str, Reading]:
69
- return await merge_gathered_dicts(
70
- [sig.read() for sig in self._read_signals]
71
- + [sig.read(cached=False) for sig in self._read_uncached_signals]
72
- )
@@ -1,18 +0,0 @@
1
- from ophyd_async.core import Device
2
-
3
- from ..signal.signal import epics_signal_rw
4
- from .utils import ImageMode, ad_r, ad_rw
5
-
6
-
7
- class ADDriver(Device):
8
- def __init__(self, prefix: str) -> None:
9
- # Define some signals
10
- self.acquire = ad_rw(bool, prefix + "Acquire")
11
- self.acquire_time = ad_rw(float, prefix + "AcquireTime")
12
- self.num_images = ad_rw(int, prefix + "NumImages")
13
- self.image_mode = ad_rw(ImageMode, prefix + "ImageMode")
14
- self.array_counter = ad_rw(int, prefix + "ArrayCounter")
15
- self.array_size_x = ad_r(int, prefix + "ArraySizeX")
16
- self.array_size_y = ad_r(int, prefix + "ArraySizeY")
17
- # There is no _RBV for this one
18
- self.wait_for_plugins = epics_signal_rw(bool, prefix + "WaitForPlugins")
@@ -1,18 +0,0 @@
1
- import tempfile
2
- from abc import abstractmethod
3
- from pathlib import Path
4
- from typing import Protocol
5
-
6
-
7
- class DirectoryProvider(Protocol):
8
- @abstractmethod
9
- async def get_directory(self) -> Path:
10
- ...
11
-
12
-
13
- class TmpDirectoryProvider(DirectoryProvider):
14
- def __init__(self) -> None:
15
- self._directory = Path(tempfile.mkdtemp())
16
-
17
- async def get_directory(self) -> Path:
18
- return self._directory
@@ -1,167 +0,0 @@
1
- import asyncio
2
- import collections
3
- import time
4
- from typing import Callable, Dict, Iterator, Optional, Sized
5
-
6
- from bluesky.protocols import (
7
- Asset,
8
- Descriptor,
9
- Flyable,
10
- PartialEvent,
11
- WritesExternalAssets,
12
- )
13
- from bluesky.utils import new_uid
14
- from event_model import compose_stream_resource
15
-
16
- from ophyd_async.core import (
17
- DEFAULT_TIMEOUT,
18
- AsyncStatus,
19
- StandardReadable,
20
- set_and_wait_for_value,
21
- )
22
-
23
- from .ad_driver import ADDriver
24
- from .directory_provider import DirectoryProvider
25
- from .nd_file_hdf import NDFileHDF
26
- from .utils import FileWriteMode, ImageMode
27
-
28
- # How long in seconds to wait between flushes of HDF datasets
29
- FLUSH_PERIOD = 0.5
30
-
31
- # How long to wait for new frames before timing out
32
- FRAME_TIMEOUT = 120
33
-
34
-
35
- class _HDFResource:
36
- def __init__(self) -> None:
37
- # TODO: set to Deque[Asset] after protocols updated for stream*
38
- # https://github.com/bluesky/bluesky/issues/1558
39
- self.asset_docs = collections.deque() # type: ignore
40
- self._last_emitted = 0
41
- self._last_flush = time.monotonic()
42
- self._compose_datum: Optional[Callable] = None
43
-
44
- def _append_resource(self, full_file_name: str):
45
- resource_doc, (self._compose_datum,) = compose_stream_resource(
46
- spec="AD_HDF5_SWMR_SLICE",
47
- root="/",
48
- resource_path=full_file_name,
49
- resource_kwargs={},
50
- stream_names=["primary"],
51
- )
52
- self.asset_docs.append(("stream_resource", resource_doc))
53
-
54
- def _append_datum(self, event_count: int):
55
- assert self._compose_datum, "Resource not emitted yet"
56
- datum_doc = self._compose_datum(
57
- datum_kwargs={},
58
- event_offset=self._last_emitted,
59
- event_count=event_count,
60
- )
61
- self._last_emitted += event_count
62
- self.asset_docs.append(("stream_datum", datum_doc))
63
-
64
- async def flush_and_publish(self, hdf: NDFileHDF):
65
- num_captured = await hdf.num_captured.get_value()
66
- if num_captured:
67
- if self._compose_datum is None:
68
- self._append_resource(await hdf.full_file_name.get_value())
69
- event_count = num_captured - self._last_emitted
70
- if event_count:
71
- self._append_datum(event_count)
72
- await hdf.flush_now.set(True)
73
- self._last_flush = time.monotonic()
74
- if time.monotonic() - self._last_flush > FRAME_TIMEOUT:
75
- raise TimeoutError(f"{hdf.name}: writing stalled on frame {num_captured}")
76
-
77
-
78
- class HDFStreamerDet(StandardReadable, Flyable, WritesExternalAssets):
79
- def __init__(
80
- self, drv: ADDriver, hdf: NDFileHDF, dp: DirectoryProvider, name=""
81
- ) -> None:
82
- self.drv = drv
83
- self.hdf = hdf
84
- self._dp = dp
85
- self._resource = _HDFResource()
86
- self._capture_status: Optional[AsyncStatus] = None
87
- self._start_status: Optional[AsyncStatus] = None
88
- self.set_readable_signals(config=[self.drv.acquire_time])
89
- super().__init__(name)
90
-
91
- @AsyncStatus.wrap
92
- async def stage(self) -> None:
93
- # Make a new resource for the new HDF file we're going to open
94
- self._resource = _HDFResource()
95
- await asyncio.gather(
96
- self.drv.wait_for_plugins.set(True),
97
- self.hdf.lazy_open.set(True),
98
- self.hdf.swmr_mode.set(True),
99
- self.hdf.file_path.set(str(await self._dp.get_directory())),
100
- self.hdf.file_name.set(f"{self.name}-{new_uid()}"),
101
- self.hdf.file_template.set("%s/%s.h5"),
102
- # Go forever
103
- self.hdf.num_capture.set(0),
104
- self.hdf.file_write_mode.set(FileWriteMode.stream),
105
- )
106
- # Wait for it to start, stashing the status that tells us when it finishes
107
- self._capture_status = await set_and_wait_for_value(self.hdf.capture, True)
108
- await super().stage()
109
-
110
- async def describe(self) -> Dict[str, Descriptor]:
111
- datakeys = await super().describe()
112
- # Insert a descriptor for the HDF resource, this will not appear
113
- # in read() as it describes StreamResource outputs only
114
- datakeys[self.name] = Descriptor(
115
- source=self.hdf.full_file_name.source,
116
- shape=await asyncio.gather(
117
- self.drv.array_size_y.get_value(),
118
- self.drv.array_size_x.get_value(),
119
- ),
120
- dtype="array",
121
- external="STREAM:",
122
- )
123
- return datakeys
124
-
125
- # For step scan, take a single frame
126
- @AsyncStatus.wrap
127
- async def trigger(self):
128
- await self.drv.image_mode.set(ImageMode.single)
129
- frame_timeout = DEFAULT_TIMEOUT + await self.drv.acquire_time.get_value()
130
- await self.drv.acquire.set(1, timeout=frame_timeout)
131
- await self._resource.flush_and_publish(self.hdf)
132
-
133
- def collect_asset_docs(self) -> Iterator[Asset]:
134
- while self._resource.asset_docs:
135
- yield self._resource.asset_docs.popleft()
136
-
137
- # For flyscan, take the number of frames we wanted
138
- @AsyncStatus.wrap
139
- async def kickoff(self) -> None:
140
- await self.drv.image_mode.set(ImageMode.multiple)
141
- # Wait for it to start, stashing the status that tells us when it finishes
142
- self._start_status = await set_and_wait_for_value(self.drv.acquire, True)
143
-
144
- # Do the same thing for flyscans and step scans
145
- async def describe_collect(self) -> Dict[str, Dict[str, Descriptor]]:
146
- return {self.name: await self.describe()}
147
-
148
- def collect(self) -> Iterator[PartialEvent]:
149
- yield from iter([])
150
-
151
- @AsyncStatus.wrap
152
- async def complete(self) -> None:
153
- done: Sized = ()
154
- while not done:
155
- assert self._start_status, "Kickoff not run"
156
- done, _ = await asyncio.wait(
157
- (self._start_status.task,), timeout=FLUSH_PERIOD
158
- )
159
- await self._resource.flush_and_publish(self.hdf)
160
-
161
- @AsyncStatus.wrap
162
- async def unstage(self) -> None:
163
- # Already done a caput callback in _capture_status, so can't do one here
164
- await self.hdf.capture.set(False, wait=False)
165
- assert self._capture_status, "Stage not run"
166
- await self._capture_status
167
- await super().unstage()
@@ -1,22 +0,0 @@
1
- from ophyd_async.core import Device
2
-
3
- from ..signal.signal import epics_signal_rw
4
- from .utils import FileWriteMode, ad_r, ad_rw
5
-
6
-
7
- class NDFileHDF(Device):
8
- def __init__(self, prefix: str) -> None:
9
- # Define some signals
10
- self.file_path = ad_rw(str, prefix + "FilePath")
11
- self.file_name = ad_rw(str, prefix + "FileName")
12
- self.file_template = ad_rw(str, prefix + "FileTemplate")
13
- self.full_file_name = ad_r(str, prefix + "FullFileName")
14
- self.file_write_mode = ad_rw(FileWriteMode, prefix + "FileWriteMode")
15
- self.num_capture = ad_rw(int, prefix + "NumCapture")
16
- self.num_captured = ad_r(int, prefix + "NumCaptured")
17
- self.swmr_mode = ad_rw(bool, prefix + "SWMRMode")
18
- self.lazy_open = ad_rw(bool, prefix + "LazyOpen")
19
- self.capture = ad_rw(bool, prefix + "Capture")
20
- self.flush_now = epics_signal_rw(bool, prefix + "FlushNow")
21
- self.array_size0 = ad_r(int, prefix + "ArraySize0")
22
- self.array_size1 = ad_r(int, prefix + "ArraySize1")
@@ -1,13 +0,0 @@
1
- from ophyd_async.core import Device
2
-
3
- from .utils import ad_r
4
-
5
-
6
- class NDPlugin(Device):
7
- pass
8
-
9
-
10
- class NDPluginStats(NDPlugin):
11
- def __init__(self, prefix: str) -> None:
12
- # Define some signals
13
- self.unique_id = ad_r(int, prefix + "UniqueId")
@@ -1,22 +0,0 @@
1
- from typing import Dict, TypedDict
2
-
3
- from p4p.client.asyncio import Context
4
-
5
-
6
- class PVIEntry(TypedDict, total=False):
7
- d: str
8
- r: str
9
- rw: str
10
- w: str
11
- x: str
12
-
13
-
14
- async def pvi_get(pv: str, ctxt: Context, timeout: float = 5.0) -> Dict[str, PVIEntry]:
15
- pv_info = ctxt.get(pv, timeout=timeout).get("pvi").todict()
16
-
17
- result = {}
18
-
19
- for attr_name, attr_info in pv_info.items():
20
- result[attr_name] = PVIEntry(**attr_info) # type: ignore
21
-
22
- return result