ophyd-async 0.4.0__py3-none-any.whl → 0.5.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 (92) hide show
  1. ophyd_async/_version.py +2 -2
  2. ophyd_async/core/__init__.py +84 -73
  3. ophyd_async/core/{detector.py → _detector.py} +4 -8
  4. ophyd_async/core/{device.py → _device.py} +1 -1
  5. ophyd_async/core/{device_save_loader.py → _device_save_loader.py} +3 -3
  6. ophyd_async/core/{flyer.py → _flyer.py} +6 -8
  7. ophyd_async/{epics/areadetector/writers/general_hdffile.py → core/_hdf_dataset.py} +4 -4
  8. ophyd_async/{log.py → core/_log.py} +11 -3
  9. ophyd_async/core/{mock_signal_backend.py → _mock_signal_backend.py} +3 -3
  10. ophyd_async/core/{mock_signal_utils.py → _mock_signal_utils.py} +3 -4
  11. ophyd_async/{protocols.py → core/_protocol.py} +1 -1
  12. ophyd_async/core/{standard_readable.py → _readable.py} +6 -16
  13. ophyd_async/core/{signal.py → _signal.py} +7 -8
  14. ophyd_async/core/{signal_backend.py → _signal_backend.py} +4 -13
  15. ophyd_async/core/{soft_signal_backend.py → _soft_signal_backend.py} +3 -12
  16. ophyd_async/core/{async_status.py → _status.py} +3 -11
  17. ophyd_async/epics/adaravis/__init__.py +9 -0
  18. ophyd_async/epics/{areadetector/aravis.py → adaravis/_aravis.py} +9 -11
  19. ophyd_async/epics/{areadetector/controllers/aravis_controller.py → adaravis/_aravis_controller.py} +7 -10
  20. ophyd_async/epics/{areadetector/drivers/aravis_driver.py → adaravis/_aravis_io.py} +6 -3
  21. ophyd_async/epics/adcore/__init__.py +36 -0
  22. ophyd_async/epics/adcore/_core_io.py +114 -0
  23. ophyd_async/epics/{areadetector/drivers/ad_base.py → adcore/_core_logic.py} +16 -52
  24. ophyd_async/epics/{areadetector/writers/hdf_writer.py → adcore/_hdf_writer.py} +12 -11
  25. ophyd_async/epics/{areadetector/single_trigger_det.py → adcore/_single_trigger.py} +5 -6
  26. ophyd_async/epics/{areadetector/utils.py → adcore/_utils.py} +29 -0
  27. ophyd_async/epics/adkinetix/__init__.py +9 -0
  28. ophyd_async/epics/{areadetector/kinetix.py → adkinetix/_kinetix.py} +9 -11
  29. ophyd_async/epics/{areadetector/controllers/kinetix_controller.py → adkinetix/_kinetix_controller.py} +6 -9
  30. ophyd_async/epics/{areadetector/drivers/kinetix_driver.py → adkinetix/_kinetix_io.py} +5 -4
  31. ophyd_async/epics/adpilatus/__init__.py +11 -0
  32. ophyd_async/epics/{areadetector/pilatus.py → adpilatus/_pilatus.py} +10 -14
  33. ophyd_async/epics/{areadetector/controllers/pilatus_controller.py → adpilatus/_pilatus_controller.py} +14 -16
  34. ophyd_async/epics/{areadetector/drivers/pilatus_driver.py → adpilatus/_pilatus_io.py} +5 -3
  35. ophyd_async/epics/adsimdetector/__init__.py +7 -0
  36. ophyd_async/epics/{demo/demo_ad_sim_detector.py → adsimdetector/_sim.py} +10 -11
  37. ophyd_async/epics/{areadetector/controllers/ad_sim_controller.py → adsimdetector/_sim_controller.py} +8 -14
  38. ophyd_async/epics/advimba/__init__.py +9 -0
  39. ophyd_async/epics/{areadetector/vimba.py → advimba/_vimba.py} +9 -9
  40. ophyd_async/epics/{areadetector/controllers/vimba_controller.py → advimba/_vimba_controller.py} +6 -14
  41. ophyd_async/epics/{areadetector/drivers/vimba_driver.py → advimba/_vimba_io.py} +5 -4
  42. ophyd_async/epics/demo/__init__.py +9 -132
  43. ophyd_async/epics/demo/_mover.py +97 -0
  44. ophyd_async/epics/demo/_sensor.py +36 -0
  45. ophyd_async/epics/{motion/motor.py → motor.py} +13 -12
  46. ophyd_async/epics/pvi/__init__.py +2 -2
  47. ophyd_async/epics/pvi/{pvi.py → _pvi.py} +17 -14
  48. ophyd_async/epics/signal/__init__.py +7 -1
  49. ophyd_async/epics/{_backend → signal}/_aioca.py +3 -2
  50. ophyd_async/epics/{_backend/common.py → signal/_common.py} +1 -1
  51. ophyd_async/epics/signal/_epics_transport.py +3 -3
  52. ophyd_async/epics/{_backend → signal}/_p4p.py +4 -3
  53. ophyd_async/epics/signal/{signal.py → _signal.py} +10 -9
  54. ophyd_async/fastcs/odin/__init__.py +0 -0
  55. ophyd_async/{panda → fastcs/panda}/__init__.py +18 -13
  56. ophyd_async/{panda → fastcs/panda}/_common_blocks.py +3 -3
  57. ophyd_async/{panda → fastcs/panda}/_hdf_panda.py +2 -7
  58. ophyd_async/{panda/writers → fastcs/panda}/_hdf_writer.py +8 -7
  59. ophyd_async/{panda → fastcs/panda}/_panda_controller.py +2 -1
  60. ophyd_async/{panda → fastcs/panda}/_trigger.py +3 -7
  61. ophyd_async/plan_stubs/__init__.py +2 -2
  62. ophyd_async/plan_stubs/{ensure_connected.py → _ensure_connected.py} +1 -2
  63. ophyd_async/plan_stubs/{fly.py → _fly.py} +13 -9
  64. ophyd_async/sim/__init__.py +0 -11
  65. ophyd_async/sim/demo/__init__.py +18 -2
  66. ophyd_async/sim/demo/_pattern_detector/__init__.py +13 -0
  67. ophyd_async/sim/{sim_pattern_generator.py → demo/_pattern_detector/_pattern_detector.py} +8 -8
  68. ophyd_async/sim/{sim_pattern_detector_control.py → demo/_pattern_detector/_pattern_detector_controller.py} +4 -5
  69. ophyd_async/sim/{sim_pattern_detector_writer.py → demo/_pattern_detector/_pattern_detector_writer.py} +4 -4
  70. ophyd_async/sim/{pattern_generator.py → demo/_pattern_detector/_pattern_generator.py} +12 -8
  71. ophyd_async/sim/demo/{sim_motor.py → _sim_motor.py} +7 -5
  72. ophyd_async/sim/testing/__init__.py +0 -0
  73. ophyd_async/tango/__init__.py +0 -0
  74. {ophyd_async-0.4.0.dist-info → ophyd_async-0.5.0.dist-info}/METADATA +1 -1
  75. ophyd_async-0.5.0.dist-info/RECORD +89 -0
  76. {ophyd_async-0.4.0.dist-info → ophyd_async-0.5.0.dist-info}/WHEEL +1 -1
  77. ophyd_async/epics/areadetector/__init__.py +0 -23
  78. ophyd_async/epics/areadetector/controllers/__init__.py +0 -5
  79. ophyd_async/epics/areadetector/drivers/__init__.py +0 -23
  80. ophyd_async/epics/areadetector/writers/__init__.py +0 -5
  81. ophyd_async/epics/areadetector/writers/nd_file_hdf.py +0 -43
  82. ophyd_async/epics/areadetector/writers/nd_plugin.py +0 -68
  83. ophyd_async/epics/motion/__init__.py +0 -3
  84. ophyd_async/panda/writers/__init__.py +0 -3
  85. ophyd_async-0.4.0.dist-info/RECORD +0 -84
  86. /ophyd_async/core/{utils.py → _utils.py} +0 -0
  87. /ophyd_async/{epics/_backend → fastcs}/__init__.py +0 -0
  88. /ophyd_async/{panda → fastcs/panda}/_table.py +0 -0
  89. /ophyd_async/{panda → fastcs/panda}/_utils.py +0 -0
  90. {ophyd_async-0.4.0.dist-info → ophyd_async-0.5.0.dist-info}/LICENSE +0 -0
  91. {ophyd_async-0.4.0.dist-info → ophyd_async-0.5.0.dist-info}/entry_points.txt +0 -0
  92. {ophyd_async-0.4.0.dist-info → ophyd_async-0.5.0.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  from enum import Enum
2
2
 
3
- from ...signal import epics_signal_r, epics_signal_rw_rbv
4
- from .ad_base import ADBase
3
+ from ophyd_async.epics import adcore
4
+ from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw_rbv
5
5
 
6
6
 
7
7
  class PilatusTriggerMode(str, Enum):
@@ -12,7 +12,9 @@ class PilatusTriggerMode(str, Enum):
12
12
  alignment = "Alignment"
13
13
 
14
14
 
15
- class PilatusDriver(ADBase):
15
+ class PilatusDriverIO(adcore.ADBaseIO):
16
+ """This mirrors the interface provided by ADPilatus/db/pilatus.template."""
17
+
16
18
  def __init__(self, prefix: str, name: str = "") -> None:
17
19
  self.trigger_mode = epics_signal_rw_rbv(
18
20
  PilatusTriggerMode, prefix + "TriggerMode"
@@ -0,0 +1,7 @@
1
+ from ._sim import SimDetector
2
+ from ._sim_controller import SimController
3
+
4
+ __all__ = [
5
+ "SimDetector",
6
+ "SimController",
7
+ ]
@@ -1,20 +1,19 @@
1
1
  from typing import Sequence
2
2
 
3
3
  from ophyd_async.core import PathProvider, SignalR, StandardDetector
4
+ from ophyd_async.epics import adcore
4
5
 
5
- from ..areadetector.controllers import ADSimController
6
- from ..areadetector.drivers import ADBase, ADBaseShapeProvider
7
- from ..areadetector.writers import HDFWriter, NDFileHDF
6
+ from ._sim_controller import SimController
8
7
 
9
8
 
10
- class DemoADSimDetector(StandardDetector):
11
- _controller: ADSimController
12
- _writer: HDFWriter
9
+ class SimDetector(StandardDetector):
10
+ _controller: SimController
11
+ _writer: adcore.ADHDFWriter
13
12
 
14
13
  def __init__(
15
14
  self,
16
- drv: ADBase,
17
- hdf: NDFileHDF,
15
+ drv: adcore.ADBaseIO,
16
+ hdf: adcore.NDFileHDFIO,
18
17
  path_provider: PathProvider,
19
18
  name: str = "",
20
19
  config_sigs: Sequence[SignalR] = (),
@@ -23,12 +22,12 @@ class DemoADSimDetector(StandardDetector):
23
22
  self.hdf = hdf
24
23
 
25
24
  super().__init__(
26
- ADSimController(self.drv),
27
- HDFWriter(
25
+ SimController(self.drv),
26
+ adcore.ADHDFWriter(
28
27
  self.hdf,
29
28
  path_provider,
30
29
  lambda: self.name,
31
- ADBaseShapeProvider(self.drv),
30
+ adcore.ADBaseShapeProvider(self.drv),
32
31
  ),
33
32
  config_sigs=config_sigs,
34
33
  name=name,
@@ -7,20 +7,14 @@ from ophyd_async.core import (
7
7
  DetectorControl,
8
8
  DetectorTrigger,
9
9
  )
10
-
11
- from ..drivers.ad_base import (
12
- DEFAULT_GOOD_STATES,
13
- ADBase,
14
- DetectorState,
15
- ImageMode,
16
- start_acquiring_driver_and_ensure_status,
17
- )
18
- from ..utils import stop_busy_record
10
+ from ophyd_async.epics import adcore
19
11
 
20
12
 
21
- class ADSimController(DetectorControl):
13
+ class SimController(DetectorControl):
22
14
  def __init__(
23
- self, driver: ADBase, good_states: Set[DetectorState] = set(DEFAULT_GOOD_STATES)
15
+ self,
16
+ driver: adcore.ADBaseIO,
17
+ good_states: Set[adcore.DetectorState] = set(adcore.DEFAULT_GOOD_STATES),
24
18
  ) -> None:
25
19
  self.driver = driver
26
20
  self.good_states = good_states
@@ -40,13 +34,13 @@ class ADSimController(DetectorControl):
40
34
  frame_timeout = DEFAULT_TIMEOUT + await self.driver.acquire_time.get_value()
41
35
  await asyncio.gather(
42
36
  self.driver.num_images.set(num),
43
- self.driver.image_mode.set(ImageMode.multiple),
37
+ self.driver.image_mode.set(adcore.ImageMode.multiple),
44
38
  )
45
- return await start_acquiring_driver_and_ensure_status(
39
+ return await adcore.start_acquiring_driver_and_ensure_status(
46
40
  self.driver, good_states=self.good_states, timeout=frame_timeout
47
41
  )
48
42
 
49
43
  async def disarm(self):
50
44
  # We can't use caput callback as we already used it in arm() and we can't have
51
45
  # 2 or they will deadlock
52
- await stop_busy_record(self.driver.acquire, False, timeout=1)
46
+ await adcore.stop_busy_record(self.driver.acquire, False, timeout=1)
@@ -0,0 +1,9 @@
1
+ from ._vimba import VimbaDetector
2
+ from ._vimba_controller import VimbaController
3
+ from ._vimba_io import VimbaDriverIO
4
+
5
+ __all__ = [
6
+ "VimbaDetector",
7
+ "VimbaController",
8
+ "VimbaDriverIO",
9
+ ]
@@ -1,10 +1,10 @@
1
1
  from bluesky.protocols import HasHints, Hints
2
2
 
3
3
  from ophyd_async.core import PathProvider, StandardDetector
4
- from ophyd_async.epics.areadetector.controllers.vimba_controller import VimbaController
5
- from ophyd_async.epics.areadetector.drivers import ADBaseShapeProvider
6
- from ophyd_async.epics.areadetector.drivers.vimba_driver import VimbaDriver
7
- from ophyd_async.epics.areadetector.writers import HDFWriter, NDFileHDF
4
+ from ophyd_async.epics import adcore
5
+
6
+ from ._vimba_controller import VimbaController
7
+ from ._vimba_io import VimbaDriverIO
8
8
 
9
9
 
10
10
  class VimbaDetector(StandardDetector, HasHints):
@@ -13,7 +13,7 @@ class VimbaDetector(StandardDetector, HasHints):
13
13
  """
14
14
 
15
15
  _controller: VimbaController
16
- _writer: HDFWriter
16
+ _writer: adcore.ADHDFWriter
17
17
 
18
18
  def __init__(
19
19
  self,
@@ -23,16 +23,16 @@ class VimbaDetector(StandardDetector, HasHints):
23
23
  hdf_suffix="HDF1:",
24
24
  name="",
25
25
  ):
26
- self.drv = VimbaDriver(prefix + drv_suffix)
27
- self.hdf = NDFileHDF(prefix + hdf_suffix)
26
+ self.drv = VimbaDriverIO(prefix + drv_suffix)
27
+ self.hdf = adcore.NDFileHDFIO(prefix + hdf_suffix)
28
28
 
29
29
  super().__init__(
30
30
  VimbaController(self.drv),
31
- HDFWriter(
31
+ adcore.ADHDFWriter(
32
32
  self.hdf,
33
33
  path_provider,
34
34
  lambda: self.name,
35
- ADBaseShapeProvider(self.drv),
35
+ adcore.ADBaseShapeProvider(self.drv),
36
36
  ),
37
37
  config_sigs=(self.drv.acquire_time,),
38
38
  name=name,
@@ -2,17 +2,9 @@ import asyncio
2
2
  from typing import Optional
3
3
 
4
4
  from ophyd_async.core import AsyncStatus, DetectorControl, DetectorTrigger
5
- from ophyd_async.epics.areadetector.drivers.ad_base import (
6
- start_acquiring_driver_and_ensure_status,
7
- )
5
+ from ophyd_async.epics import adcore
8
6
 
9
- from ..drivers.vimba_driver import (
10
- VimbaDriver,
11
- VimbaExposeOutMode,
12
- VimbaOnOff,
13
- VimbaTriggerSource,
14
- )
15
- from ..utils import ImageMode, stop_busy_record
7
+ from ._vimba_io import VimbaDriverIO, VimbaExposeOutMode, VimbaOnOff, VimbaTriggerSource
16
8
 
17
9
  TRIGGER_MODE = {
18
10
  DetectorTrigger.internal: VimbaOnOff.off,
@@ -32,7 +24,7 @@ EXPOSE_OUT_MODE = {
32
24
  class VimbaController(DetectorControl):
33
25
  def __init__(
34
26
  self,
35
- driver: VimbaDriver,
27
+ driver: VimbaDriverIO,
36
28
  ) -> None:
37
29
  self._drv = driver
38
30
 
@@ -49,7 +41,7 @@ class VimbaController(DetectorControl):
49
41
  self._drv.trigger_mode.set(TRIGGER_MODE[trigger]),
50
42
  self._drv.expose_mode.set(EXPOSE_OUT_MODE[trigger]),
51
43
  self._drv.num_images.set(num),
52
- self._drv.image_mode.set(ImageMode.multiple),
44
+ self._drv.image_mode.set(adcore.ImageMode.multiple),
53
45
  )
54
46
  if exposure is not None and trigger not in [
55
47
  DetectorTrigger.variable_gate,
@@ -60,7 +52,7 @@ class VimbaController(DetectorControl):
60
52
  self._drv.trig_source.set(VimbaTriggerSource.line1)
61
53
  else:
62
54
  self._drv.trig_source.set(VimbaTriggerSource.freerun)
63
- return await start_acquiring_driver_and_ensure_status(self._drv)
55
+ return await adcore.start_acquiring_driver_and_ensure_status(self._drv)
64
56
 
65
57
  async def disarm(self):
66
- await stop_busy_record(self._drv.acquire, False, timeout=1)
58
+ await adcore.stop_busy_record(self._drv.acquire, False, timeout=1)
@@ -1,8 +1,7 @@
1
1
  from enum import Enum
2
2
 
3
- from ophyd_async.epics.signal.signal import epics_signal_rw_rbv
4
-
5
- from .ad_base import ADBase
3
+ from ophyd_async.epics import adcore
4
+ from ophyd_async.epics.signal import epics_signal_rw_rbv
6
5
 
7
6
 
8
7
  class VimbaPixelFormat(str, Enum):
@@ -46,7 +45,9 @@ class VimbaExposeOutMode(str, Enum):
46
45
  trigger_width = "TriggerWidth" # Expose for length of high signal
47
46
 
48
47
 
49
- class VimbaDriver(ADBase):
48
+ class VimbaDriverIO(adcore.ADBaseIO):
49
+ """This mirrors the interface provided by ADVimba/db/vimba.template."""
50
+
50
51
  def __init__(self, prefix: str, name: str = "") -> None:
51
52
  # self.pixel_format = epics_signal_rw_rbv(PixelFormat, prefix + "PixelFormat")
52
53
  self.convert_format = epics_signal_rw_rbv(
@@ -1,145 +1,22 @@
1
1
  """Demo EPICS Devices for the tutorial"""
2
2
 
3
- import asyncio
4
3
  import atexit
5
4
  import random
6
5
  import string
7
6
  import subprocess
8
7
  import sys
9
- from enum import Enum
10
8
  from pathlib import Path
11
9
 
12
- import numpy as np
13
- from bluesky.protocols import Movable, Stoppable
10
+ from ._mover import Mover, SampleStage
11
+ from ._sensor import EnergyMode, Sensor, SensorGroup
14
12
 
15
- from ophyd_async.core import (
16
- ConfigSignal,
17
- Device,
18
- DeviceVector,
19
- HintedSignal,
20
- StandardReadable,
21
- WatchableAsyncStatus,
22
- observe_value,
23
- )
24
- from ophyd_async.core.async_status import AsyncStatus
25
- from ophyd_async.core.utils import (
26
- DEFAULT_TIMEOUT,
27
- CalculatableTimeout,
28
- CalculateTimeout,
29
- WatcherUpdate,
30
- )
31
-
32
- from ..signal.signal import epics_signal_r, epics_signal_rw, epics_signal_x
33
-
34
-
35
- class EnergyMode(str, Enum):
36
- """Energy mode for `Sensor`"""
37
-
38
- #: Low energy mode
39
- low = "Low Energy"
40
- #: High energy mode
41
- high = "High Energy"
42
-
43
-
44
- class Sensor(StandardReadable):
45
- """A demo sensor that produces a scalar value based on X and Y Movers"""
46
-
47
- def __init__(self, prefix: str, name="") -> None:
48
- # Define some signals
49
- with self.add_children_as_readables(HintedSignal):
50
- self.value = epics_signal_r(float, prefix + "Value")
51
- with self.add_children_as_readables(ConfigSignal):
52
- self.mode = epics_signal_rw(EnergyMode, prefix + "Mode")
53
-
54
- super().__init__(name=name)
55
-
56
-
57
- class SensorGroup(StandardReadable):
58
- def __init__(self, prefix: str, name: str = "", sensor_count: int = 3) -> None:
59
- with self.add_children_as_readables():
60
- self.sensors = DeviceVector(
61
- {i: Sensor(f"{prefix}{i}:") for i in range(1, sensor_count + 1)}
62
- )
63
-
64
- super().__init__(name)
65
-
66
-
67
- class Mover(StandardReadable, Movable, Stoppable):
68
- """A demo movable that moves based on velocity"""
69
-
70
- def __init__(self, prefix: str, name="") -> None:
71
- # Define some signals
72
- with self.add_children_as_readables(HintedSignal):
73
- self.readback = epics_signal_r(float, prefix + "Readback")
74
- with self.add_children_as_readables(ConfigSignal):
75
- self.velocity = epics_signal_rw(float, prefix + "Velocity")
76
- self.units = epics_signal_r(str, prefix + "Readback.EGU")
77
- self.setpoint = epics_signal_rw(float, prefix + "Setpoint")
78
- self.precision = epics_signal_r(int, prefix + "Readback.PREC")
79
- # Signals that collide with standard methods should have a trailing underscore
80
- self.stop_ = epics_signal_x(prefix + "Stop.PROC")
81
- # Whether set() should complete successfully or not
82
- self._set_success = True
83
-
84
- super().__init__(name=name)
85
-
86
- def set_name(self, name: str):
87
- super().set_name(name)
88
- # Readback should be named the same as its parent in read()
89
- self.readback.set_name(name)
90
-
91
- @WatchableAsyncStatus.wrap
92
- async def set(
93
- self, new_position: float, timeout: CalculatableTimeout = CalculateTimeout
94
- ):
95
- self._set_success = True
96
- old_position, units, precision, velocity = await asyncio.gather(
97
- self.setpoint.get_value(),
98
- self.units.get_value(),
99
- self.precision.get_value(),
100
- self.velocity.get_value(),
101
- )
102
- if timeout is CalculateTimeout:
103
- assert velocity > 0, "Mover has zero velocity"
104
- timeout = abs(new_position - old_position) / velocity + DEFAULT_TIMEOUT
105
- # Make an Event that will be set on completion, and a Status that will
106
- # error if not done in time
107
- done = asyncio.Event()
108
- done_status = AsyncStatus(asyncio.wait_for(done.wait(), timeout))
109
- # Wait for the value to set, but don't wait for put completion callback
110
- await self.setpoint.set(new_position, wait=False)
111
- async for current_position in observe_value(
112
- self.readback, done_status=done_status
113
- ):
114
- yield WatcherUpdate(
115
- current=current_position,
116
- initial=old_position,
117
- target=new_position,
118
- name=self.name,
119
- unit=units,
120
- precision=precision,
121
- )
122
- if np.isclose(current_position, new_position):
123
- done.set()
124
- break
125
- if not self._set_success:
126
- raise RuntimeError("Motor was stopped")
127
-
128
- async def stop(self, success=True):
129
- self._set_success = success
130
- status = self.stop_.trigger()
131
- await status
132
-
133
-
134
- class SampleStage(Device):
135
- """A demo sample stage with X and Y movables"""
136
-
137
- def __init__(self, prefix: str, name="") -> None:
138
- # Define some child Devices
139
- self.x = Mover(prefix + "X:")
140
- self.y = Mover(prefix + "Y:")
141
- # Set name of device and child devices
142
- super().__init__(name=name)
13
+ __all__ = [
14
+ "Mover",
15
+ "SampleStage",
16
+ "EnergyMode",
17
+ "Sensor",
18
+ "SensorGroup",
19
+ ]
143
20
 
144
21
 
145
22
  def start_ioc_subprocess() -> str:
@@ -0,0 +1,97 @@
1
+ import asyncio
2
+
3
+ import numpy as np
4
+ from bluesky.protocols import Movable, Stoppable
5
+
6
+ from ophyd_async.core import (
7
+ DEFAULT_TIMEOUT,
8
+ AsyncStatus,
9
+ CalculatableTimeout,
10
+ CalculateTimeout,
11
+ ConfigSignal,
12
+ Device,
13
+ HintedSignal,
14
+ StandardReadable,
15
+ WatchableAsyncStatus,
16
+ WatcherUpdate,
17
+ observe_value,
18
+ )
19
+ from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw, epics_signal_x
20
+
21
+
22
+ class Mover(StandardReadable, Movable, Stoppable):
23
+ """A demo movable that moves based on velocity"""
24
+
25
+ def __init__(self, prefix: str, name="") -> None:
26
+ # Define some signals
27
+ with self.add_children_as_readables(HintedSignal):
28
+ self.readback = epics_signal_r(float, prefix + "Readback")
29
+ with self.add_children_as_readables(ConfigSignal):
30
+ self.velocity = epics_signal_rw(float, prefix + "Velocity")
31
+ self.units = epics_signal_r(str, prefix + "Readback.EGU")
32
+ self.setpoint = epics_signal_rw(float, prefix + "Setpoint")
33
+ self.precision = epics_signal_r(int, prefix + "Readback.PREC")
34
+ # Signals that collide with standard methods should have a trailing underscore
35
+ self.stop_ = epics_signal_x(prefix + "Stop.PROC")
36
+ # Whether set() should complete successfully or not
37
+ self._set_success = True
38
+
39
+ super().__init__(name=name)
40
+
41
+ def set_name(self, name: str):
42
+ super().set_name(name)
43
+ # Readback should be named the same as its parent in read()
44
+ self.readback.set_name(name)
45
+
46
+ @WatchableAsyncStatus.wrap
47
+ async def set(
48
+ self, new_position: float, timeout: CalculatableTimeout = CalculateTimeout
49
+ ):
50
+ self._set_success = True
51
+ old_position, units, precision, velocity = await asyncio.gather(
52
+ self.setpoint.get_value(),
53
+ self.units.get_value(),
54
+ self.precision.get_value(),
55
+ self.velocity.get_value(),
56
+ )
57
+ if timeout is CalculateTimeout:
58
+ assert velocity > 0, "Mover has zero velocity"
59
+ timeout = abs(new_position - old_position) / velocity + DEFAULT_TIMEOUT
60
+ # Make an Event that will be set on completion, and a Status that will
61
+ # error if not done in time
62
+ done = asyncio.Event()
63
+ done_status = AsyncStatus(asyncio.wait_for(done.wait(), timeout))
64
+ # Wait for the value to set, but don't wait for put completion callback
65
+ await self.setpoint.set(new_position, wait=False)
66
+ async for current_position in observe_value(
67
+ self.readback, done_status=done_status
68
+ ):
69
+ yield WatcherUpdate(
70
+ current=current_position,
71
+ initial=old_position,
72
+ target=new_position,
73
+ name=self.name,
74
+ unit=units,
75
+ precision=precision,
76
+ )
77
+ if np.isclose(current_position, new_position):
78
+ done.set()
79
+ break
80
+ if not self._set_success:
81
+ raise RuntimeError("Motor was stopped")
82
+
83
+ async def stop(self, success=True):
84
+ self._set_success = success
85
+ status = self.stop_.trigger()
86
+ await status
87
+
88
+
89
+ class SampleStage(Device):
90
+ """A demo sample stage with X and Y movables"""
91
+
92
+ def __init__(self, prefix: str, name="") -> None:
93
+ # Define some child Devices
94
+ self.x = Mover(prefix + "X:")
95
+ self.y = Mover(prefix + "Y:")
96
+ # Set name of device and child devices
97
+ super().__init__(name=name)
@@ -0,0 +1,36 @@
1
+ from enum import Enum
2
+
3
+ from ophyd_async.core import ConfigSignal, DeviceVector, HintedSignal, StandardReadable
4
+ from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw
5
+
6
+
7
+ class EnergyMode(str, Enum):
8
+ """Energy mode for `Sensor`"""
9
+
10
+ #: Low energy mode
11
+ low = "Low Energy"
12
+ #: High energy mode
13
+ high = "High Energy"
14
+
15
+
16
+ class Sensor(StandardReadable):
17
+ """A demo sensor that produces a scalar value based on X and Y Movers"""
18
+
19
+ def __init__(self, prefix: str, name="") -> None:
20
+ # Define some signals
21
+ with self.add_children_as_readables(HintedSignal):
22
+ self.value = epics_signal_r(float, prefix + "Value")
23
+ with self.add_children_as_readables(ConfigSignal):
24
+ self.mode = epics_signal_rw(EnergyMode, prefix + "Mode")
25
+
26
+ super().__init__(name=name)
27
+
28
+
29
+ class SensorGroup(StandardReadable):
30
+ def __init__(self, prefix: str, name: str = "", sensor_count: int = 3) -> None:
31
+ with self.add_children_as_readables():
32
+ self.sensors = DeviceVector(
33
+ {i: Sensor(f"{prefix}{i}:") for i in range(1, sensor_count + 1)}
34
+ )
35
+
36
+ super().__init__(name)
@@ -5,21 +5,18 @@ from bluesky.protocols import Flyable, Movable, Preparable, Stoppable
5
5
  from pydantic import BaseModel, Field
6
6
 
7
7
  from ophyd_async.core import (
8
+ DEFAULT_TIMEOUT,
9
+ AsyncStatus,
10
+ CalculatableTimeout,
11
+ CalculateTimeout,
8
12
  ConfigSignal,
9
13
  HintedSignal,
10
14
  StandardReadable,
11
15
  WatchableAsyncStatus,
12
- )
13
- from ophyd_async.core.async_status import AsyncStatus
14
- from ophyd_async.core.signal import observe_value
15
- from ophyd_async.core.utils import (
16
- DEFAULT_TIMEOUT,
17
- CalculatableTimeout,
18
- CalculateTimeout,
19
16
  WatcherUpdate,
17
+ observe_value,
20
18
  )
21
-
22
- from ..signal.signal import epics_signal_r, epics_signal_rw, epics_signal_x
19
+ from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw, epics_signal_x
23
20
 
24
21
 
25
22
  class MotorLimitsException(Exception):
@@ -116,6 +113,7 @@ class Motor(StandardReadable, Movable, Stoppable, Flyable, Preparable):
116
113
  )
117
114
 
118
115
  await self.set(fly_prepared_position)
116
+ await self.velocity.set(fly_velocity)
119
117
 
120
118
  @AsyncStatus.wrap
121
119
  async def kickoff(self):
@@ -182,7 +180,7 @@ class Motor(StandardReadable, Movable, Stoppable, Flyable, Preparable):
182
180
  async def _prepare_velocity(
183
181
  self, start_position: float, end_position: float, time_for_move: float
184
182
  ) -> float:
185
- fly_velocity = (start_position - end_position) / time_for_move
183
+ fly_velocity = (end_position - start_position) / time_for_move
186
184
  max_speed, egu = await asyncio.gather(
187
185
  self.max_velocity.get_value(), self.motor_egu.get_value()
188
186
  )
@@ -191,7 +189,8 @@ class Motor(StandardReadable, Movable, Stoppable, Flyable, Preparable):
191
189
  f"Motor speed of {abs(fly_velocity)} {egu}/s was requested for a motor "
192
190
  f" with max speed of {max_speed} {egu}/s"
193
191
  )
194
- await self.velocity.set(abs(fly_velocity))
192
+ # move to prepare position at maximum velocity
193
+ await self.velocity.set(abs(max_speed))
195
194
  return fly_velocity
196
195
 
197
196
  async def _prepare_motor_path(
@@ -199,7 +198,9 @@ class Motor(StandardReadable, Movable, Stoppable, Flyable, Preparable):
199
198
  ) -> float:
200
199
  # Distance required for motor to accelerate from stationary to fly_velocity, and
201
200
  # distance required for motor to decelerate from fly_velocity to stationary
202
- run_up_distance = (await self.acceleration_time.get_value()) * fly_velocity
201
+ run_up_distance = (
202
+ (await self.acceleration_time.get_value()) * fly_velocity * 0.5
203
+ )
203
204
 
204
205
  self._fly_completed_position = end_position + run_up_distance
205
206
 
@@ -1,3 +1,3 @@
1
- from .pvi import PVIEntry, create_children_from_annotations, fill_pvi_entries
1
+ from ._pvi import create_children_from_annotations, fill_pvi_entries
2
2
 
3
- __all__ = ["PVIEntry", "fill_pvi_entries", "create_children_from_annotations"]
3
+ __all__ = ["fill_pvi_entries", "create_children_from_annotations"]