ophyd-async 0.3a2__py3-none-any.whl → 0.3a4__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.
- ophyd_async/_version.py +1 -1
- ophyd_async/core/__init__.py +35 -11
- ophyd_async/core/async_status.py +2 -0
- ophyd_async/core/detector.py +8 -9
- ophyd_async/core/device.py +22 -9
- ophyd_async/core/flyer.py +2 -2
- ophyd_async/core/mock_signal_backend.py +86 -0
- ophyd_async/core/mock_signal_utils.py +149 -0
- ophyd_async/core/signal.py +140 -49
- ophyd_async/core/signal_backend.py +2 -2
- ophyd_async/core/{sim_signal_backend.py → soft_signal_backend.py} +29 -39
- ophyd_async/core/standard_readable.py +211 -24
- ophyd_async/epics/_backend/_aioca.py +17 -13
- ophyd_async/epics/_backend/_p4p.py +28 -18
- ophyd_async/epics/_backend/common.py +17 -17
- ophyd_async/epics/areadetector/__init__.py +4 -4
- ophyd_async/epics/areadetector/aravis.py +7 -9
- ophyd_async/epics/areadetector/controllers/__init__.py +2 -1
- ophyd_async/epics/areadetector/controllers/kinetix_controller.py +49 -0
- ophyd_async/epics/areadetector/controllers/vimba_controller.py +66 -0
- ophyd_async/epics/areadetector/drivers/__init__.py +6 -0
- ophyd_async/epics/areadetector/drivers/ad_base.py +12 -10
- ophyd_async/epics/areadetector/drivers/aravis_driver.py +7 -5
- ophyd_async/epics/areadetector/drivers/kinetix_driver.py +27 -0
- ophyd_async/epics/areadetector/drivers/pilatus_driver.py +5 -2
- ophyd_async/epics/areadetector/drivers/vimba_driver.py +63 -0
- ophyd_async/epics/areadetector/kinetix.py +46 -0
- ophyd_async/epics/areadetector/pilatus.py +7 -12
- ophyd_async/epics/areadetector/single_trigger_det.py +14 -6
- ophyd_async/epics/areadetector/utils.py +2 -12
- ophyd_async/epics/areadetector/vimba.py +43 -0
- ophyd_async/epics/areadetector/writers/hdf_writer.py +6 -3
- ophyd_async/epics/areadetector/writers/nd_file_hdf.py +21 -18
- ophyd_async/epics/areadetector/writers/nd_plugin.py +6 -7
- ophyd_async/epics/demo/__init__.py +19 -22
- ophyd_async/epics/motion/motor.py +16 -13
- ophyd_async/epics/pvi/pvi.py +11 -11
- ophyd_async/epics/signal/signal.py +1 -1
- ophyd_async/log.py +130 -0
- ophyd_async/panda/_hdf_panda.py +3 -3
- ophyd_async/panda/writers/_hdf_writer.py +3 -3
- ophyd_async/protocols.py +26 -3
- ophyd_async/sim/demo/sim_motor.py +14 -12
- ophyd_async/sim/pattern_generator.py +9 -9
- ophyd_async/sim/sim_pattern_detector_writer.py +2 -2
- ophyd_async/sim/sim_pattern_generator.py +2 -2
- {ophyd_async-0.3a2.dist-info → ophyd_async-0.3a4.dist-info}/METADATA +20 -3
- ophyd_async-0.3a4.dist-info/RECORD +85 -0
- ophyd_async-0.3a2.dist-info/RECORD +0 -76
- {ophyd_async-0.3a2.dist-info → ophyd_async-0.3a4.dist-info}/LICENSE +0 -0
- {ophyd_async-0.3a2.dist-info → ophyd_async-0.3a4.dist-info}/WHEEL +0 -0
- {ophyd_async-0.3a2.dist-info → ophyd_async-0.3a4.dist-info}/entry_points.txt +0 -0
- {ophyd_async-0.3a2.dist-info → ophyd_async-0.3a4.dist-info}/top_level.txt +0 -0
ophyd_async/core/signal.py
CHANGED
|
@@ -2,27 +2,36 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import functools
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import (
|
|
6
|
+
Any,
|
|
7
|
+
AsyncGenerator,
|
|
8
|
+
Callable,
|
|
9
|
+
Dict,
|
|
10
|
+
Generic,
|
|
11
|
+
Mapping,
|
|
12
|
+
Optional,
|
|
13
|
+
Tuple,
|
|
14
|
+
Type,
|
|
15
|
+
Union,
|
|
16
|
+
)
|
|
6
17
|
|
|
7
18
|
from bluesky.protocols import (
|
|
8
|
-
|
|
19
|
+
DataKey,
|
|
9
20
|
Locatable,
|
|
10
21
|
Location,
|
|
11
22
|
Movable,
|
|
12
23
|
Reading,
|
|
13
|
-
Stageable,
|
|
14
24
|
Subscribable,
|
|
15
25
|
)
|
|
16
26
|
|
|
17
|
-
from ophyd_async.
|
|
27
|
+
from ophyd_async.core.mock_signal_backend import MockSignalBackend
|
|
28
|
+
from ophyd_async.protocols import AsyncConfigurable, AsyncReadable, AsyncStageable
|
|
18
29
|
|
|
19
30
|
from .async_status import AsyncStatus
|
|
20
31
|
from .device import Device
|
|
21
32
|
from .signal_backend import SignalBackend
|
|
22
|
-
from .
|
|
23
|
-
from .utils import DEFAULT_TIMEOUT, Callback,
|
|
24
|
-
|
|
25
|
-
_sim_backends: Dict[Signal, SimSignalBackend] = {}
|
|
33
|
+
from .soft_signal_backend import SoftSignalBackend
|
|
34
|
+
from .utils import DEFAULT_TIMEOUT, Callback, T
|
|
26
35
|
|
|
27
36
|
|
|
28
37
|
def _add_timeout(func):
|
|
@@ -51,17 +60,17 @@ class Signal(Device, Generic[T]):
|
|
|
51
60
|
timeout: Optional[float] = DEFAULT_TIMEOUT,
|
|
52
61
|
name: str = "",
|
|
53
62
|
) -> None:
|
|
54
|
-
super().__init__(name)
|
|
55
63
|
self._timeout = timeout
|
|
56
|
-
self.
|
|
64
|
+
self._initial_backend = self._backend = backend
|
|
65
|
+
super().__init__(name)
|
|
57
66
|
|
|
58
|
-
async def connect(self,
|
|
59
|
-
if
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
async def connect(self, mock=False, timeout=DEFAULT_TIMEOUT):
|
|
68
|
+
if mock and not isinstance(self._backend, MockSignalBackend):
|
|
69
|
+
# Using a soft backend, look to the initial value
|
|
70
|
+
self._backend = MockSignalBackend(
|
|
71
|
+
initial_backend=self._initial_backend,
|
|
72
|
+
)
|
|
73
|
+
self.log.debug(f"Connecting to {self.source}")
|
|
65
74
|
await self._backend.connect(timeout=timeout)
|
|
66
75
|
|
|
67
76
|
@property
|
|
@@ -86,10 +95,12 @@ class _SignalCache(Generic[T]):
|
|
|
86
95
|
self._value: Optional[T] = None
|
|
87
96
|
|
|
88
97
|
self.backend = backend
|
|
98
|
+
signal.log.debug(f"Making subscription on source {signal.source}")
|
|
89
99
|
backend.set_callback(self._callback)
|
|
90
100
|
|
|
91
101
|
def close(self):
|
|
92
102
|
self.backend.set_callback(None)
|
|
103
|
+
self._signal.log.debug(f"Closing subscription on source {self._signal.source}")
|
|
93
104
|
|
|
94
105
|
async def get_reading(self) -> Reading:
|
|
95
106
|
await self._valid.wait()
|
|
@@ -102,6 +113,10 @@ class _SignalCache(Generic[T]):
|
|
|
102
113
|
return self._value
|
|
103
114
|
|
|
104
115
|
def _callback(self, reading: Reading, value: T):
|
|
116
|
+
self._signal.log.debug(
|
|
117
|
+
f"Updated subscription: reading of source {self._signal.source} changed"
|
|
118
|
+
f"from {self._reading} to {reading}"
|
|
119
|
+
)
|
|
105
120
|
self._reading = reading
|
|
106
121
|
self._value = value
|
|
107
122
|
self._valid.set()
|
|
@@ -128,7 +143,7 @@ class _SignalCache(Generic[T]):
|
|
|
128
143
|
return self._staged or bool(self._listeners)
|
|
129
144
|
|
|
130
145
|
|
|
131
|
-
class SignalR(Signal[T], AsyncReadable,
|
|
146
|
+
class SignalR(Signal[T], AsyncReadable, AsyncStageable, Subscribable):
|
|
132
147
|
"""Signal that can be read from and monitored"""
|
|
133
148
|
|
|
134
149
|
_cache: Optional[_SignalCache] = None
|
|
@@ -161,14 +176,16 @@ class SignalR(Signal[T], AsyncReadable, Stageable, Subscribable):
|
|
|
161
176
|
return {self.name: await self._backend_or_cache(cached).get_reading()}
|
|
162
177
|
|
|
163
178
|
@_add_timeout
|
|
164
|
-
async def describe(self) -> Dict[str,
|
|
179
|
+
async def describe(self) -> Dict[str, DataKey]:
|
|
165
180
|
"""Return a single item dict with the descriptor in it"""
|
|
166
|
-
return {self.name: await self._backend.
|
|
181
|
+
return {self.name: await self._backend.get_datakey(self.source)}
|
|
167
182
|
|
|
168
183
|
@_add_timeout
|
|
169
184
|
async def get_value(self, cached: Optional[bool] = None) -> T:
|
|
170
185
|
"""The current value"""
|
|
171
|
-
|
|
186
|
+
value = await self._backend_or_cache(cached).get_value()
|
|
187
|
+
self.log.debug(f"get_value() on source {self.source} returned {value}")
|
|
188
|
+
return value
|
|
172
189
|
|
|
173
190
|
def subscribe_value(self, function: Callback[T]):
|
|
174
191
|
"""Subscribe to updates in value of a device"""
|
|
@@ -203,8 +220,15 @@ class SignalW(Signal[T], Movable):
|
|
|
203
220
|
"""Set the value and return a status saying when it's done"""
|
|
204
221
|
if timeout is USE_DEFAULT_TIMEOUT:
|
|
205
222
|
timeout = self._timeout
|
|
206
|
-
|
|
207
|
-
|
|
223
|
+
|
|
224
|
+
async def do_set():
|
|
225
|
+
self.log.debug(f"Putting value {value} to backend at source {self.source}")
|
|
226
|
+
await self._backend.put(value, wait=wait, timeout=timeout)
|
|
227
|
+
self.log.debug(
|
|
228
|
+
f"Successfully put value {value} to backend at source {self.source}"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
return AsyncStatus(do_set())
|
|
208
232
|
|
|
209
233
|
|
|
210
234
|
class SignalRW(SignalR[T], SignalW[T], Locatable):
|
|
@@ -229,47 +253,114 @@ class SignalX(Signal):
|
|
|
229
253
|
return AsyncStatus(coro)
|
|
230
254
|
|
|
231
255
|
|
|
232
|
-
def set_sim_value(signal: Signal[T], value: T):
|
|
233
|
-
"""Set the value of a signal that is in sim mode."""
|
|
234
|
-
_sim_backends[signal]._set_value(value)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
def set_sim_put_proceeds(signal: Signal[T], proceeds: bool):
|
|
238
|
-
"""Allow or block a put with wait=True from proceeding"""
|
|
239
|
-
event = _sim_backends[signal].put_proceeds
|
|
240
|
-
if proceeds:
|
|
241
|
-
event.set()
|
|
242
|
-
else:
|
|
243
|
-
event.clear()
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
def set_sim_callback(signal: Signal[T], callback: ReadingValueCallback[T]) -> None:
|
|
247
|
-
"""Monitor the value of a signal that is in sim mode"""
|
|
248
|
-
return _sim_backends[signal].set_callback(callback)
|
|
249
|
-
|
|
250
|
-
|
|
251
256
|
def soft_signal_rw(
|
|
252
257
|
datatype: Optional[Type[T]] = None,
|
|
253
258
|
initial_value: Optional[T] = None,
|
|
254
259
|
name: str = "",
|
|
255
260
|
) -> SignalRW[T]:
|
|
256
|
-
"""Creates a read-writable Signal with a
|
|
257
|
-
signal = SignalRW(
|
|
261
|
+
"""Creates a read-writable Signal with a SoftSignalBackend"""
|
|
262
|
+
signal = SignalRW(SoftSignalBackend(datatype, initial_value), name=name)
|
|
258
263
|
return signal
|
|
259
264
|
|
|
260
265
|
|
|
261
|
-
def
|
|
266
|
+
def soft_signal_r_and_setter(
|
|
262
267
|
datatype: Optional[Type[T]] = None,
|
|
263
268
|
initial_value: Optional[T] = None,
|
|
264
269
|
name: str = "",
|
|
265
|
-
) -> Tuple[SignalR[T],
|
|
266
|
-
"""Returns a tuple of a read-only Signal and
|
|
270
|
+
) -> Tuple[SignalR[T], Callable[[T]]]:
|
|
271
|
+
"""Returns a tuple of a read-only Signal and a callable through
|
|
267
272
|
which the signal can be internally modified within the device. Use
|
|
268
273
|
soft_signal_rw if you want a device that is externally modifiable
|
|
269
274
|
"""
|
|
270
|
-
backend =
|
|
275
|
+
backend = SoftSignalBackend(datatype, initial_value)
|
|
271
276
|
signal = SignalR(backend, name=name)
|
|
272
|
-
|
|
277
|
+
|
|
278
|
+
return (signal, backend.set_value)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
async def assert_value(signal: SignalR[T], value: Any) -> None:
|
|
282
|
+
"""Assert a signal's value and compare it an expected signal.
|
|
283
|
+
|
|
284
|
+
Parameters
|
|
285
|
+
----------
|
|
286
|
+
signal:
|
|
287
|
+
signal with get_value.
|
|
288
|
+
value:
|
|
289
|
+
The expected value from the signal.
|
|
290
|
+
|
|
291
|
+
Notes
|
|
292
|
+
-----
|
|
293
|
+
Example usage::
|
|
294
|
+
await assert_value(signal, value)
|
|
295
|
+
|
|
296
|
+
"""
|
|
297
|
+
assert await signal.get_value() == value
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
async def assert_reading(
|
|
301
|
+
readable: AsyncReadable, reading: Mapping[str, Reading]
|
|
302
|
+
) -> None:
|
|
303
|
+
"""Assert readings from readable.
|
|
304
|
+
|
|
305
|
+
Parameters
|
|
306
|
+
----------
|
|
307
|
+
readable:
|
|
308
|
+
Callable with readable.read function that generate readings.
|
|
309
|
+
|
|
310
|
+
reading:
|
|
311
|
+
The expected readings from the readable.
|
|
312
|
+
|
|
313
|
+
Notes
|
|
314
|
+
-----
|
|
315
|
+
Example usage::
|
|
316
|
+
await assert_reading(readable, reading)
|
|
317
|
+
|
|
318
|
+
"""
|
|
319
|
+
assert await readable.read() == reading
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
async def assert_configuration(
|
|
323
|
+
configurable: AsyncConfigurable,
|
|
324
|
+
configuration: Mapping[str, Reading],
|
|
325
|
+
) -> None:
|
|
326
|
+
"""Assert readings from Configurable.
|
|
327
|
+
|
|
328
|
+
Parameters
|
|
329
|
+
----------
|
|
330
|
+
configurable:
|
|
331
|
+
Configurable with Configurable.read function that generate readings.
|
|
332
|
+
|
|
333
|
+
configuration:
|
|
334
|
+
The expected readings from configurable.
|
|
335
|
+
|
|
336
|
+
Notes
|
|
337
|
+
-----
|
|
338
|
+
Example usage::
|
|
339
|
+
await assert_configuration(configurable configuration)
|
|
340
|
+
|
|
341
|
+
"""
|
|
342
|
+
assert await configurable.read_configuration() == configuration
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def assert_emitted(docs: Mapping[str, list[dict]], **numbers: int):
|
|
346
|
+
"""Assert emitted document generated by running a Bluesky plan
|
|
347
|
+
|
|
348
|
+
Parameters
|
|
349
|
+
----------
|
|
350
|
+
Doc:
|
|
351
|
+
A dictionary
|
|
352
|
+
|
|
353
|
+
numbers:
|
|
354
|
+
expected emission in kwarg from
|
|
355
|
+
|
|
356
|
+
Notes
|
|
357
|
+
-----
|
|
358
|
+
Example usage::
|
|
359
|
+
assert_emitted(docs, start=1, descriptor=1,
|
|
360
|
+
resource=1, datum=1, event=1, stop=1)
|
|
361
|
+
"""
|
|
362
|
+
assert list(docs) == list(numbers)
|
|
363
|
+
assert {name: len(d) for name, d in docs.items()} == numbers
|
|
273
364
|
|
|
274
365
|
|
|
275
366
|
async def observe_value(signal: SignalR[T], timeout=None) -> AsyncGenerator[T, None]:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
from typing import Generic, Optional, Type
|
|
3
3
|
|
|
4
|
-
from bluesky.protocols import
|
|
4
|
+
from bluesky.protocols import DataKey, Reading
|
|
5
5
|
|
|
6
6
|
from .utils import DEFAULT_TIMEOUT, ReadingValueCallback, T
|
|
7
7
|
|
|
@@ -27,7 +27,7 @@ class SignalBackend(Generic[T]):
|
|
|
27
27
|
"""Put a value to the PV, if wait then wait for completion for up to timeout"""
|
|
28
28
|
|
|
29
29
|
@abstractmethod
|
|
30
|
-
async def
|
|
30
|
+
async def get_datakey(self, source: str) -> DataKey:
|
|
31
31
|
"""Metadata like source, dtype, shape, precision, units"""
|
|
32
32
|
|
|
33
33
|
@abstractmethod
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import inspect
|
|
5
4
|
import time
|
|
6
5
|
from collections import abc
|
|
7
6
|
from dataclasses import dataclass
|
|
8
7
|
from enum import Enum
|
|
9
|
-
from typing import
|
|
8
|
+
from typing import Dict, Generic, Optional, Type, Union, cast, get_origin
|
|
10
9
|
|
|
11
10
|
import numpy as np
|
|
12
|
-
from bluesky.protocols import
|
|
11
|
+
from bluesky.protocols import DataKey, Dtype, Reading
|
|
13
12
|
|
|
14
13
|
from .signal_backend import SignalBackend
|
|
15
14
|
from .utils import DEFAULT_TIMEOUT, ReadingValueCallback, T, get_dtype
|
|
@@ -22,7 +21,7 @@ primitive_dtypes: Dict[type, Dtype] = {
|
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
class
|
|
24
|
+
class SoftConverter(Generic[T]):
|
|
26
25
|
def value(self, value: T) -> T:
|
|
27
26
|
return value
|
|
28
27
|
|
|
@@ -36,7 +35,7 @@ class SimConverter(Generic[T]):
|
|
|
36
35
|
alarm_severity=-1 if severity > 2 else severity,
|
|
37
36
|
)
|
|
38
37
|
|
|
39
|
-
def
|
|
38
|
+
def get_datakey(self, source: str, value) -> DataKey:
|
|
40
39
|
dtype = type(value)
|
|
41
40
|
if np.issubdtype(dtype, np.integer):
|
|
42
41
|
dtype = int
|
|
@@ -55,8 +54,8 @@ class SimConverter(Generic[T]):
|
|
|
55
54
|
return datatype()
|
|
56
55
|
|
|
57
56
|
|
|
58
|
-
class
|
|
59
|
-
def
|
|
57
|
+
class SoftArrayConverter(SoftConverter):
|
|
58
|
+
def get_datakey(self, source: str, value) -> DataKey:
|
|
60
59
|
return {"source": source, "dtype": "array", "shape": [len(value)]}
|
|
61
60
|
|
|
62
61
|
def make_initial_value(self, datatype: Optional[Type[T]]) -> T:
|
|
@@ -70,7 +69,7 @@ class SimArrayConverter(SimConverter):
|
|
|
70
69
|
|
|
71
70
|
|
|
72
71
|
@dataclass
|
|
73
|
-
class
|
|
72
|
+
class SoftEnumConverter(SoftConverter):
|
|
74
73
|
enum_class: Type[Enum]
|
|
75
74
|
|
|
76
75
|
def write_value(self, value: Union[Enum, str]) -> Enum:
|
|
@@ -79,7 +78,7 @@ class SimEnumConverter(SimConverter):
|
|
|
79
78
|
else:
|
|
80
79
|
return self.enum_class(value)
|
|
81
80
|
|
|
82
|
-
def
|
|
81
|
+
def get_datakey(self, source: str, value) -> DataKey:
|
|
83
82
|
choices = [e.value for e in self.enum_class]
|
|
84
83
|
return {"source": source, "dtype": "string", "shape": [], "choices": choices} # type: ignore
|
|
85
84
|
|
|
@@ -90,26 +89,21 @@ class SimEnumConverter(SimConverter):
|
|
|
90
89
|
return cast(T, list(datatype.__members__.values())[0]) # type: ignore
|
|
91
90
|
|
|
92
91
|
|
|
93
|
-
class DisconnectedSimConverter(SimConverter):
|
|
94
|
-
def __getattribute__(self, __name: str) -> Any:
|
|
95
|
-
raise NotImplementedError("No PV has been set as connect() has not been called")
|
|
96
|
-
|
|
97
|
-
|
|
98
92
|
def make_converter(datatype):
|
|
99
93
|
is_array = get_dtype(datatype) is not None
|
|
100
94
|
is_sequence = get_origin(datatype) == abc.Sequence
|
|
101
95
|
is_enum = issubclass(datatype, Enum) if inspect.isclass(datatype) else False
|
|
102
96
|
|
|
103
97
|
if is_array or is_sequence:
|
|
104
|
-
return
|
|
98
|
+
return SoftArrayConverter()
|
|
105
99
|
if is_enum:
|
|
106
|
-
return
|
|
100
|
+
return SoftEnumConverter(datatype)
|
|
107
101
|
|
|
108
|
-
return
|
|
102
|
+
return SoftConverter()
|
|
109
103
|
|
|
110
104
|
|
|
111
|
-
class
|
|
112
|
-
"""An
|
|
105
|
+
class SoftSignalBackend(SignalBackend[T]):
|
|
106
|
+
"""An backend to a soft Signal, for test signals see ``MockSignalBackend``."""
|
|
113
107
|
|
|
114
108
|
_value: T
|
|
115
109
|
_initial_value: Optional[T]
|
|
@@ -122,25 +116,23 @@ class SimSignalBackend(SignalBackend[T]):
|
|
|
122
116
|
initial_value: Optional[T] = None,
|
|
123
117
|
) -> None:
|
|
124
118
|
self.datatype = datatype
|
|
125
|
-
self.converter: SimConverter = DisconnectedSimConverter()
|
|
126
119
|
self._initial_value = initial_value
|
|
127
|
-
self.
|
|
128
|
-
self.put_proceeds.set()
|
|
129
|
-
self.callback: Optional[ReadingValueCallback[T]] = None
|
|
130
|
-
|
|
131
|
-
def source(self, name: str) -> str:
|
|
132
|
-
return f"soft://{name}"
|
|
133
|
-
|
|
134
|
-
async def connect(self, timeout: float = DEFAULT_TIMEOUT) -> None:
|
|
135
|
-
self.converter = make_converter(self.datatype)
|
|
120
|
+
self.converter: SoftConverter = make_converter(datatype)
|
|
136
121
|
if self._initial_value is None:
|
|
137
122
|
self._initial_value = self.converter.make_initial_value(self.datatype)
|
|
138
123
|
else:
|
|
139
|
-
# convert potentially unconverted initial value passed to init method
|
|
140
124
|
self._initial_value = self.converter.write_value(self._initial_value)
|
|
125
|
+
|
|
126
|
+
self.callback: Optional[ReadingValueCallback[T]] = None
|
|
141
127
|
self._severity = 0
|
|
128
|
+
self.set_value(self._initial_value)
|
|
142
129
|
|
|
143
|
-
|
|
130
|
+
def source(self, name: str) -> str:
|
|
131
|
+
return f"soft://{name}"
|
|
132
|
+
|
|
133
|
+
async def connect(self, timeout: float = DEFAULT_TIMEOUT) -> None:
|
|
134
|
+
"""Connection isn't required for soft signals."""
|
|
135
|
+
pass
|
|
144
136
|
|
|
145
137
|
async def put(self, value: Optional[T], wait=True, timeout=None):
|
|
146
138
|
write_value = (
|
|
@@ -148,13 +140,11 @@ class SimSignalBackend(SignalBackend[T]):
|
|
|
148
140
|
if value is not None
|
|
149
141
|
else self._initial_value
|
|
150
142
|
)
|
|
151
|
-
self._set_value(write_value)
|
|
152
143
|
|
|
153
|
-
|
|
154
|
-
await asyncio.wait_for(self.put_proceeds.wait(), timeout)
|
|
144
|
+
self.set_value(write_value)
|
|
155
145
|
|
|
156
|
-
def
|
|
157
|
-
"""Method to bypass asynchronous logic
|
|
146
|
+
def set_value(self, value: T):
|
|
147
|
+
"""Method to bypass asynchronous logic."""
|
|
158
148
|
self._value = value
|
|
159
149
|
self._timestamp = time.monotonic()
|
|
160
150
|
reading: Reading = self.converter.reading(
|
|
@@ -164,8 +154,8 @@ class SimSignalBackend(SignalBackend[T]):
|
|
|
164
154
|
if self.callback:
|
|
165
155
|
self.callback(reading, self._value)
|
|
166
156
|
|
|
167
|
-
async def
|
|
168
|
-
return self.converter.
|
|
157
|
+
async def get_datakey(self, source: str) -> DataKey:
|
|
158
|
+
return self.converter.get_datakey(source, self._value)
|
|
169
159
|
|
|
170
160
|
async def get_reading(self) -> Reading:
|
|
171
161
|
return self.converter.reading(self._value, self._timestamp, self._severity)
|
|
@@ -174,7 +164,7 @@ class SimSignalBackend(SignalBackend[T]):
|
|
|
174
164
|
return self.converter.value(self._value)
|
|
175
165
|
|
|
176
166
|
async def get_setpoint(self) -> T:
|
|
177
|
-
"""For a
|
|
167
|
+
"""For a soft signal, the setpoint and readback values are the same."""
|
|
178
168
|
return await self.get_value()
|
|
179
169
|
|
|
180
170
|
def set_callback(self, callback: Optional[ReadingValueCallback[T]]) -> None:
|