Skip to content

Commit 10917fd

Browse files
committed
Added config signal only plans to handle settings including get_current_config_settings and retrieve_config_settings.
1 parent 8bba961 commit 10917fd

File tree

5 files changed

+137
-7
lines changed

5 files changed

+137
-7
lines changed

src/ophyd_async/plan_stubs/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
from ._settings import (
1212
apply_settings,
1313
apply_settings_if_different,
14+
get_current_config_settings,
1415
get_current_settings,
16+
retrieve_config_settings,
1517
retrieve_settings,
1618
store_config_settings,
1719
store_settings,
@@ -28,7 +30,9 @@
2830
"apply_settings",
2931
"apply_settings_if_different",
3032
"get_current_settings",
33+
"get_current_config_settings",
3134
"retrieve_settings",
35+
"retrieve_config_settings",
3236
"store_settings",
3337
"store_config_settings",
3438
]

src/ophyd_async/plan_stubs/_settings.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ def get_current_settings(device: Device) -> MsgGenerator[Settings]:
4141
return Settings(device, signal_values)
4242

4343

44+
@plan
45+
def get_current_config_settings(device: Device) -> MsgGenerator[Settings]:
46+
"""Get current configuration settings on `Configurable`."""
47+
signals = yield from wait_for_awaitable(walk_config_signals(device))
48+
named_values = yield from _get_values_of_signals(signals)
49+
signal_values = {signals[name]: value for name, value in named_values.items()}
50+
return Settings(device, signal_values)
51+
52+
4453
@plan
4554
def store_settings(
4655
provider: SettingsProvider, name: str, device: Device
@@ -60,7 +69,7 @@ def store_settings(
6069
def store_config_settings(
6170
provider: SettingsProvider, name: str, device: Device
6271
) -> MsgGenerator[None]:
63-
"""Walk a Device for SignalRWs and store their values.
72+
"""Walk a Device for configuration attributes and store their values.
6473
6574
:param provider: The provider to store the settings with.
6675
:param name: The name to store the settings under.
@@ -85,6 +94,20 @@ def retrieve_settings(
8594
return Settings(device, signal_values)
8695

8796

97+
@plan
98+
def retrieve_config_settings(
99+
provider: SettingsProvider, name: str, device: Device
100+
) -> MsgGenerator[Settings]:
101+
"""Retrieve named configuration attribute Settings for a Device from a provider."""
102+
named_values = yield from wait_for_awaitable(provider.retrieve(name))
103+
signals = yield from wait_for_awaitable(walk_config_signals(device))
104+
unknown_names = set(named_values) - set(signals)
105+
if unknown_names:
106+
raise NameError(f"Unknown signal names {sorted(unknown_names)}")
107+
signal_values = {signals[name]: value for name, value in named_values.items()}
108+
return Settings(device, signal_values)
109+
110+
88111
@plan
89112
def apply_settings(settings: Settings) -> MsgGenerator[None]:
90113
"""Set every SignalRW to the given value in Settings. If value is None ignore it."""

src/ophyd_async/testing/_one_of_everything.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ def __init__(self, name=""):
105105
self.ndarray = soft_signal_rw(np.ndarray, np.array(([1, 2, 3], [4, 5, 6])))
106106
super().__init__(name)
107107

108+
async def get_signal_values(self):
109+
return await _get_signal_values(self)
110+
108111

109112
async def _get_signal_values(child: Device) -> dict[SignalRW, Any]:
110113
if isinstance(child, SignalRW):

tests/plan_stubs/test_settings.py

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
from unittest.mock import call
33

44
import bluesky.plan_stubs as bps
5+
import numpy as np
56
import pytest
67
import yaml
78

89
from ophyd_async.core import Settings, YamlSettingsProvider
910
from ophyd_async.plan_stubs import (
1011
apply_settings,
1112
apply_settings_if_different,
13+
get_current_config_settings,
1214
get_current_settings,
15+
retrieve_config_settings,
1316
retrieve_settings,
1417
store_config_settings,
1518
store_settings,
@@ -31,6 +34,13 @@ async def parent_device() -> ParentOfEverythingDevice:
3134
return device
3235

3336

37+
@pytest.fixture
38+
async def every_parent_device() -> OneOfEverythingDevice:
39+
device = OneOfEverythingDevice("parent")
40+
await device.connect(mock=True)
41+
return device
42+
43+
3444
async def test_get_current_settings(RE, parent_device: ParentOfEverythingDevice):
3545
expected_values = await parent_device.get_signal_values()
3646

@@ -41,6 +51,23 @@ def my_plan():
4151
RE(my_plan())
4252

4353

54+
async def test_get_current_config_settings(
55+
RE, every_parent_device: OneOfEverythingDevice
56+
):
57+
expected_values = await every_parent_device.get_signal_values()
58+
59+
def my_plan():
60+
current_settings = yield from get_current_config_settings(every_parent_device)
61+
current_settings = dict(current_settings)
62+
for key, value in current_settings.items():
63+
if isinstance(value, np.ndarray):
64+
assert np.array_equal(value, expected_values[key])
65+
else:
66+
assert value == expected_values[key]
67+
68+
RE(my_plan())
69+
70+
4471
async def test_store_settings(RE, parent_device: ParentOfEverythingDevice, tmp_path):
4572
provider = YamlSettingsProvider(tmp_path)
4673

@@ -54,19 +81,16 @@ def my_plan():
5481

5582

5683
async def test_store_config_settings(
57-
RE, parent_device: OneOfEverythingDevice, tmp_path
84+
RE, every_parent_device: OneOfEverythingDevice, tmp_path
5885
):
5986
provider = YamlSettingsProvider(tmp_path)
6087

6188
def my_plan():
62-
yield from store_config_settings(provider, "test_file", parent_device)
89+
yield from store_config_settings(provider, "test_file", every_parent_device)
6390
with open(tmp_path / "test_file.yaml") as actual_file:
6491
actual_data = yaml.safe_load(actual_file)
65-
with open(TEST_DATA / "test_yaml_save.yaml") as expected_file:
92+
with open(TEST_DATA / "test_yaml_config_save.yaml") as expected_file:
6693
expected_data = yaml.safe_load(expected_file)
67-
# Remove the keys that shouldn't be expected
68-
expected_data.pop("_sig_rw", None)
69-
expected_data.pop("sig_rw", None)
7094
assert actual_data == expected_data
7195

7296
RE(my_plan())
@@ -108,6 +132,43 @@ def my_plan():
108132
RE(my_plan())
109133

110134

135+
async def test_retrieve_and_apply_config_settings(
136+
RE, every_parent_device: OneOfEverythingDevice
137+
):
138+
provider = YamlSettingsProvider(TEST_DATA)
139+
expected_values = await every_parent_device.get_signal_values()
140+
serialized_values = {}
141+
# Override the table to be the serialized version so it compares equal
142+
for sig, value in expected_values.items():
143+
if isinstance(value, ExampleTable):
144+
serialized_values[sig] = {
145+
k: pytest.approx(v) for k, v in value.model_dump().items()
146+
}
147+
else:
148+
serialized_values[sig] = pytest.approx(value)
149+
150+
def my_plan():
151+
m = get_mock(every_parent_device)
152+
settings = yield from retrieve_config_settings(
153+
provider, "test_yaml_config_save", every_parent_device
154+
)
155+
assert dict(settings) == serialized_values
156+
assert not m.mock_calls
157+
yield from apply_settings(settings)
158+
assert len(m.mock_calls) == 19
159+
m.reset_mock()
160+
assert not m.mock_calls
161+
yield from apply_settings_if_different(settings, apply_settings)
162+
assert not m.mock_calls
163+
yield from bps.abs_set(every_parent_device.a_str, "foo", wait=True)
164+
assert m.mock_calls == [call.a_str.put("foo", wait=True)]
165+
m.reset_mock()
166+
yield from apply_settings_if_different(settings, apply_settings)
167+
assert m.mock_calls == [call.a_str.put("test_string", wait=True)]
168+
169+
RE(my_plan())
170+
171+
111172
async def test_ignored_settings(RE, parent_device: ParentOfEverythingDevice):
112173
def my_plan():
113174
m = get_mock(parent_device)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
a_bool: true
2+
a_float: 1.234
3+
a_int: 1
4+
a_str: test_string
5+
enum: Bbb
6+
enuma:
7+
- Aaa
8+
- Ccc
9+
float32a: [-3.4028234663852886e+38, 3.4028234663852886e+38, 1.1754943508222875e-38,
10+
1.401298464324817e-45, 0.0, 1.2339999675750732, 234000.0, 3.4499998946557753e-06]
11+
float64a: [-1.7976931348623157e+308, 1.7976931348623157e+308, 2.2250738585072014e-308,
12+
5.0e-324, 0.0, 1.234, 234000.0, 3.45e-06]
13+
int16a: [-32768, 32767, 0, 1, 2, 3, 4]
14+
int32a: [-2147483648, 2147483647, 0, 1, 2, 3, 4]
15+
int64a: [-9223372036854775808, 9223372036854775807, 0, 1, 2, 3, 4]
16+
int8a: [-128, 127, 0, 1, 2, 3, 4]
17+
ndarray: [[1, 2, 3], [4, 5, 6]]
18+
stra:
19+
- one
20+
- two
21+
- three
22+
table:
23+
a_bool: [false, false, true, true]
24+
a_enum:
25+
- Aaa
26+
- Bbb
27+
- Aaa
28+
- Ccc
29+
a_float: [1.8, 8.2, -6.0, 32.9887]
30+
a_int: [1, 8, -9, 32]
31+
a_str:
32+
- Hello
33+
- World
34+
- Foo
35+
- Bar
36+
uint16a: [0, 65535, 0, 1, 2, 3, 4]
37+
uint32a: [0, 4294967295, 0, 1, 2, 3, 4]
38+
uint64a: [0, 18446744073709551615, 0, 1, 2, 3, 4]
39+
uint8a: [0, 255, 0, 1, 2, 3, 4]

0 commit comments

Comments
 (0)