bec-widgets 0.44.5__py3-none-any.whl → 0.45.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.
- bec_widgets/cli/client.py +100 -1
- bec_widgets/cli/generate_cli.py +2 -1
- bec_widgets/widgets/__init__.py +1 -1
- bec_widgets/widgets/figure/figure.py +72 -1
- bec_widgets/widgets/plots/__init__.py +1 -0
- bec_widgets/widgets/plots/motor_map.py +423 -0
- bec_widgets/widgets/plots/plot_base.py +1 -1
- bec_widgets/widgets/plots/waveform1d.py +3 -2
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.45.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.45.0.dist-info}/RECORD +21 -19
- tests/client_mocks.py +76 -31
- tests/test_bec_figure.py +12 -1
- tests/test_bec_monitor.py +2 -66
- tests/test_bec_motor_map.py +125 -0
- tests/test_config_dialog.py +2 -63
- tests/test_motor_control.py +17 -83
- tests/test_motor_map.py +9 -66
- tests/test_waveform1d.py +26 -8
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.45.0.dist-info}/LICENSE +0 -0
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.45.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.45.0.dist-info}/top_level.txt +0 -0
@@ -15,7 +15,7 @@ from qtpy.QtCore import Slot as pyqtSlot
|
|
15
15
|
from qtpy.QtWidgets import QWidget
|
16
16
|
|
17
17
|
from bec_widgets.utils import BECConnector, Colors, ConnectionConfig, EntryValidator
|
18
|
-
from bec_widgets.widgets.plots import BECPlotBase, WidgetConfig
|
18
|
+
from bec_widgets.widgets.plots.plot_base import BECPlotBase, WidgetConfig
|
19
19
|
|
20
20
|
|
21
21
|
class SignalData(BaseModel):
|
@@ -25,13 +25,14 @@ class SignalData(BaseModel):
|
|
25
25
|
entry: str
|
26
26
|
unit: Optional[str] = None # todo implement later
|
27
27
|
modifier: Optional[str] = None # todo implement later
|
28
|
+
limits: Optional[list[float]] = None # todo implement later
|
28
29
|
|
29
30
|
|
30
31
|
class Signal(BaseModel):
|
31
32
|
"""The configuration of a signal in the 1D waveform widget."""
|
32
33
|
|
33
34
|
source: str
|
34
|
-
x: SignalData
|
35
|
+
x: SignalData # TODO maybe add metadata for config gui later
|
35
36
|
y: SignalData
|
36
37
|
|
37
38
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
bec_widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
2
|
bec_widgets/cli/__init__.py,sha256=yFyAmDteCcndbReunhnrfDib6JujQ7-BKWeVvuU0Ylw,30
|
3
|
-
bec_widgets/cli/client.py,sha256=
|
3
|
+
bec_widgets/cli/client.py,sha256=J3yvhteGky9XHXMoyN0am75uAE4qeo7Oj_dZbdtMHRM,37300
|
4
4
|
bec_widgets/cli/client_utils.py,sha256=8tVOmAMo5x0tUywTfivvCt8n0tJoT5WzoCa3GtsZ-qE,9694
|
5
|
-
bec_widgets/cli/generate_cli.py,sha256=
|
5
|
+
bec_widgets/cli/generate_cli.py,sha256=QqTacqaG3zYi-Zmun62j-4Tv-Az5rDbvFXA2ESFjWZ0,4010
|
6
6
|
bec_widgets/cli/server.py,sha256=4552dbbdJlalIllQGJFR3AsN0iLl5Qs23BAeqQiof0s,4721
|
7
7
|
bec_widgets/examples/__init__.py,sha256=WWQ0cu7m8sA4Ehy-DWdTIqSISjaHsbxhsNmNrMnhDZU,202
|
8
8
|
bec_widgets/examples/eiger_plot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -36,11 +36,11 @@ bec_widgets/utils/widget_io.py,sha256=JKl508VnqQSxcaHqKaoBQ1TWSOm3pXhxQGx7iF_pRA
|
|
36
36
|
bec_widgets/utils/yaml_dialog.py,sha256=soZI8BOjlqYGfYDga70MEvkxJTsktq4y7B3uog2cSik,1851
|
37
37
|
bec_widgets/validation/__init__.py,sha256=ismd1bU5FhFb0zFPwNKuq7oT48G4Y2GfaMZOdNKUtGk,132
|
38
38
|
bec_widgets/validation/monitor_config_validator.py,sha256=M9p8K_nvxicnqJB4X7j90R377WHYVH4wMCtSXsRI51M,8150
|
39
|
-
bec_widgets/widgets/__init__.py,sha256=
|
39
|
+
bec_widgets/widgets/__init__.py,sha256=QNCuDcg0o3uvGbf74qXeFjEsgIzOeEcd1hoWabJ4tKQ,437
|
40
40
|
bec_widgets/widgets/editor/__init__.py,sha256=5mBdFYi_IpygCz81kbLEZUWhd1b6oqiO3nASejuV_ug,30
|
41
41
|
bec_widgets/widgets/editor/editor.py,sha256=pIIYLPqqqhXqT11Xj10cyGEiy-ieNGE4ZujN5lf0e68,15110
|
42
42
|
bec_widgets/widgets/figure/__init__.py,sha256=3hGx_KOV7QHCYAV06aNuUgKq4QIYCjUTad-DrwkUaBM,44
|
43
|
-
bec_widgets/widgets/figure/figure.py,sha256
|
43
|
+
bec_widgets/widgets/figure/figure.py,sha256=-3SJu93gE73ttrfxD9NGPRmgvaZjH0jOX_7ovhrHXck,32454
|
44
44
|
bec_widgets/widgets/figure/figure_debug_minimal.ui,sha256=GodXBvBvs5QAUsHbo3pcxR4o51Tvce4DTqpTluk3hOs,742
|
45
45
|
bec_widgets/widgets/monitor/__init__.py,sha256=afXuZcBOxNAuYdCkIQXX5J60R5A3Q_86lNEW2vpFtPI,32
|
46
46
|
bec_widgets/widgets/monitor/config_dialog.py,sha256=Z1a4WRIVlfEGdwC-QG25kba2EHCZWi5J843tBVZlWiI,20275
|
@@ -57,40 +57,42 @@ bec_widgets/widgets/motor_control/motor_control_selection.ui,sha256=vXXpvNWuL6xy
|
|
57
57
|
bec_widgets/widgets/motor_control/motor_control_table.ui,sha256=t6aRKiSmutMfp0AyupavbCs0cal-FANEnlKQiPzC9PQ,2792
|
58
58
|
bec_widgets/widgets/motor_map/__init__.py,sha256=K3c-3A_LbxK0UJ0_bV3opL-wGLTwBLendsJXsg8GAqE,32
|
59
59
|
bec_widgets/widgets/motor_map/motor_map.py,sha256=A7VOs8RAiXaLZF4_oxOYJX5BwMhRvir0k0u5eEPFtLc,21909
|
60
|
-
bec_widgets/widgets/plots/__init__.py,sha256=
|
60
|
+
bec_widgets/widgets/plots/__init__.py,sha256=ncGc6re47T9Bq75RQrPrk2rX2V89QZWRFOwZjmvkQPw,241
|
61
61
|
bec_widgets/widgets/plots/image.py,sha256=Ty-8etIYBQl1MJLcuwCtlLUcAuXbxBq4RLDikbTL34c,31748
|
62
|
-
bec_widgets/widgets/plots/
|
63
|
-
bec_widgets/widgets/plots/
|
62
|
+
bec_widgets/widgets/plots/motor_map.py,sha256=yBqVvLgEQhiIpQyvmsoTUToEyLblVmS2sTST7N8hNQA,15115
|
63
|
+
bec_widgets/widgets/plots/plot_base.py,sha256=tF0oamgCVIXFrfD8jnMVTltkzYLLSQ0Dq9HAG57Ji3E,8446
|
64
|
+
bec_widgets/widgets/plots/waveform1d.py,sha256=RbI5dQlbAaC4A1WuQLS1DWpk5UjeELpaUEvHkFvZAbA,25631
|
64
65
|
bec_widgets/widgets/scan_control/__init__.py,sha256=IOfHl15vxb_uC6KN62-PeUzbBha_vQyqkkXbJ2HU674,38
|
65
66
|
bec_widgets/widgets/scan_control/scan_control.py,sha256=tbO9tbVynRvs4VCxTZ4ZFBDTVAojIr-zkl70vuHbWgw,17116
|
66
67
|
bec_widgets/widgets/toolbar/__init__.py,sha256=d-TP4_cr_VbpwreMM4ePnfZ5YXsEPQ45ibEf75nuGoE,36
|
67
68
|
bec_widgets/widgets/toolbar/toolbar.py,sha256=sxz7rbc8XNPS6n2WMObF4-2PqdYfPxVtsOZEGV6mqa0,5124
|
68
69
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
69
|
-
tests/client_mocks.py,sha256=
|
70
|
+
tests/client_mocks.py,sha256=925yW3vFnS8h6ym_4kpgX299WGVeObkGneuiXNWRE7s,3596
|
70
71
|
tests/conftest.py,sha256=wMFVnerrLZjpi-OnPf7Ugah_DCb51QD1WWcbR4e_M6k,1055
|
71
72
|
tests/test_bec_connector.py,sha256=f2XXGGw3NoZLIUrDuZuEWwF_ttOYmmquCgUrV5XkIOY,1951
|
72
73
|
tests/test_bec_dispatcher.py,sha256=vWQGdUyMvssrOyiVxdsOpErOh4awX_NSQWvKSBoRfAI,9223
|
73
|
-
tests/test_bec_figure.py,sha256
|
74
|
-
tests/test_bec_monitor.py,sha256=
|
74
|
+
tests/test_bec_figure.py,sha256=-2KYedk32pCsXNmAN0OFUrQT101YV3rrSL0ET3KXjjA,7464
|
75
|
+
tests/test_bec_monitor.py,sha256=mN7gBY7oXY6j65zzihpy8r-FvwVoCQlie3F6SoVq0mo,7042
|
75
76
|
tests/test_bec_monitor_scatter2D.py,sha256=UYs0pe9uBX7GpFVkhxw-Hx_MwTz7NKMtWIBqONTZ1JI,5797
|
76
|
-
tests/
|
77
|
+
tests/test_bec_motor_map.py,sha256=5D-xPQQ731Q6wsEzAmqJqoy_FpHtlK1siwqql8sJ0p0,4613
|
78
|
+
tests/test_config_dialog.py,sha256=5uNGcpvrx8qDdMwFCTXr8HMzFZF4rFi-ZHoDpMxGMf8,6955
|
77
79
|
tests/test_crosshair.py,sha256=d7fX-ymboZPALNqqiAj86PZ96llmGZ_3jf0yjVP0S94,5039
|
78
80
|
tests/test_editor.py,sha256=TED5k1xFJHRZ4KDAg2VxSRu_hMJnra-lbAmVwsDicsM,6784
|
79
81
|
tests/test_eiger_plot.py,sha256=bWnKBQid0YcLMQeBLy6ojb4ZpwTG-rFVT0kMg9Y08p8,4427
|
80
82
|
tests/test_generate_cli_client.py,sha256=BdpTZMNUFOBJa2e-rme9AJUoXfueYyLiUCOpGi3SNvc,2400
|
81
|
-
tests/test_motor_control.py,sha256=
|
82
|
-
tests/test_motor_map.py,sha256=
|
83
|
+
tests/test_motor_control.py,sha256=jdTG35z3jOL9XCAIDNIGfdv60vcwGLHa3KJjKqJkoZw,20322
|
84
|
+
tests/test_motor_map.py,sha256=UEjmtIYI2mxq9BUeopqoqNNy7UiPJEts9h45ufsFcrA,5979
|
83
85
|
tests/test_plot_base.py,sha256=bOdlgAxh9oKk5PwiQ_MSFmzr44uJ61Tlg242RCIhl5c,2610
|
84
86
|
tests/test_scan_control.py,sha256=e6_YpyxcayK35jXBSfxinGNL_8G-jcrWap25eg3z4QI,7560
|
85
87
|
tests/test_stream_plot.py,sha256=LNCYIj9CafremGaz-DwDktCRJRrjgfOdVewCUwwZE5s,5843
|
86
88
|
tests/test_validator_errors.py,sha256=NFxyv0TIOXeZKZRRUBfVQ7bpunwY4KkG95yTUdQmvns,3532
|
87
|
-
tests/test_waveform1d.py,sha256=
|
89
|
+
tests/test_waveform1d.py,sha256=GzlQTAjbg35mMKhopLCiHy3fIx0ld6WTr46ZuMTeSTE,13644
|
88
90
|
tests/test_widget_io.py,sha256=FeL3ZYSBQnRt6jxj8VGYw1cmcicRQyHKleahw7XIyR0,3475
|
89
91
|
tests/test_yaml_dialog.py,sha256=HNrqferkdg02-9ieOhhI2mr2Qvt7GrYgXmQ061YCTbg,5794
|
90
92
|
tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
91
93
|
tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
|
92
|
-
bec_widgets-0.
|
93
|
-
bec_widgets-0.
|
94
|
-
bec_widgets-0.
|
95
|
-
bec_widgets-0.
|
96
|
-
bec_widgets-0.
|
94
|
+
bec_widgets-0.45.0.dist-info/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
95
|
+
bec_widgets-0.45.0.dist-info/METADATA,sha256=9EPs7EAqxS-MNM5INN1K7kdqlawrd_oeyHwDhotp5wI,3714
|
96
|
+
bec_widgets-0.45.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
97
|
+
bec_widgets-0.45.0.dist-info/top_level.txt,sha256=EXCwhJYmXmd1DjYYL3hrGsddX-97IwYSiIHrf27FFVk,18
|
98
|
+
bec_widgets-0.45.0.dist-info/RECORD,,
|
tests/client_mocks.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# pylint: disable = no-name-in-module,missing-class-docstring, missing-module-docstring
|
2
|
-
|
3
|
-
from unittest.mock import MagicMock
|
2
|
+
from unittest.mock import MagicMock, patch
|
4
3
|
|
5
4
|
import pytest
|
5
|
+
from bec_lib.device import Positioner
|
6
|
+
from bec_lib.devicemanager import DeviceContainer
|
6
7
|
|
7
8
|
|
8
9
|
class FakeDevice:
|
@@ -12,7 +13,7 @@ class FakeDevice:
|
|
12
13
|
self.name = name
|
13
14
|
self.enabled = enabled
|
14
15
|
self.signals = {self.name: {"value": 1.0}}
|
15
|
-
self.description = {self.name: {"source": self.name}}
|
16
|
+
self.description = {self.name: {"source": self.name, "dtype": "number", "shape": []}}
|
16
17
|
|
17
18
|
def __contains__(self, item):
|
18
19
|
return item == self.name
|
@@ -38,41 +39,85 @@ class FakeDevice:
|
|
38
39
|
return self.description
|
39
40
|
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
class FakePositioner(FakeDevice):
|
43
|
+
def __init__(self, name, enabled=True, limits=None, read_value=1.0):
|
44
|
+
super().__init__(name, enabled)
|
45
|
+
self.limits = limits if limits is not None else [0, 0]
|
46
|
+
self.read_value = read_value
|
47
|
+
|
48
|
+
def set_read_value(self, value):
|
49
|
+
self.read_value = value
|
50
|
+
|
51
|
+
def read(self):
|
52
|
+
return {self.name: {"value": self.read_value}}
|
53
|
+
|
54
|
+
def set_limits(self, limits):
|
55
|
+
self.limits = limits
|
56
|
+
|
57
|
+
def move(self, value, relative=False):
|
58
|
+
"""Simulates moving the device to a new position."""
|
59
|
+
if relative:
|
60
|
+
self.read_value += value
|
61
|
+
else:
|
62
|
+
self.read_value = value
|
63
|
+
# Respect the limits
|
64
|
+
self.read_value = max(min(self.read_value, self.limits[1]), self.limits[0])
|
65
|
+
|
66
|
+
@property
|
67
|
+
def readback(self):
|
68
|
+
return MagicMock(get=MagicMock(return_value=self.read_value))
|
69
|
+
|
70
|
+
|
71
|
+
class DMMock:
|
72
|
+
def __init__(self):
|
73
|
+
self.devices = DeviceContainer()
|
74
|
+
|
75
|
+
def add_devives(self, devices: list):
|
76
|
+
for device in devices:
|
77
|
+
self.devices[device.name] = device
|
78
|
+
|
79
|
+
|
80
|
+
DEVICES = [
|
81
|
+
FakePositioner("samx", limits=[-10, 10], read_value=2.0),
|
82
|
+
FakePositioner("samy", limits=[-5, 5], read_value=3.0),
|
83
|
+
FakePositioner("aptrx", limits=None, read_value=4.0),
|
84
|
+
FakePositioner("aptry", limits=None, read_value=5.0),
|
85
|
+
FakeDevice("gauss_bpm"),
|
86
|
+
FakeDevice("gauss_adc1"),
|
87
|
+
FakeDevice("gauss_adc2"),
|
88
|
+
FakeDevice("gauss_adc3"),
|
89
|
+
FakeDevice("bpm4i"),
|
90
|
+
FakeDevice("bpm3a"),
|
91
|
+
FakeDevice("bpm3i"),
|
92
|
+
]
|
48
93
|
|
49
94
|
|
50
95
|
@pytest.fixture(scope="function")
|
51
96
|
def mocked_client():
|
52
|
-
# Create a dictionary of mocked devices
|
53
|
-
device_names = [
|
54
|
-
"samx",
|
55
|
-
"samy",
|
56
|
-
"gauss_bpm",
|
57
|
-
"gauss_adc1",
|
58
|
-
"gauss_adc2",
|
59
|
-
"gauss_adc3",
|
60
|
-
"bpm4i",
|
61
|
-
"bpm3a",
|
62
|
-
"bpm3i",
|
63
|
-
]
|
64
|
-
mocked_devices = {name: get_mocked_device(name) for name in device_names}
|
65
|
-
|
66
97
|
# Create a MagicMock object
|
67
98
|
client = MagicMock()
|
68
99
|
|
69
100
|
# Mock the device_manager.devices attribute
|
70
|
-
client.device_manager
|
71
|
-
client.device_manager.
|
72
|
-
|
101
|
+
client.device_manager = DMMock()
|
102
|
+
client.device_manager.add_devives(DEVICES)
|
103
|
+
|
104
|
+
def mock_mv(*args, relative=False):
|
105
|
+
# Extracting motor and value pairs
|
106
|
+
for i in range(0, len(args), 2):
|
107
|
+
motor = args[i]
|
108
|
+
value = args[i + 1]
|
109
|
+
motor.move(value, relative=relative)
|
110
|
+
return MagicMock(wait=MagicMock())
|
111
|
+
|
112
|
+
client.scans = MagicMock(mv=mock_mv)
|
113
|
+
|
114
|
+
# Ensure isinstance check for Positioner passes
|
115
|
+
original_isinstance = isinstance
|
73
116
|
|
74
|
-
|
75
|
-
|
76
|
-
|
117
|
+
def isinstance_mock(obj, class_info):
|
118
|
+
if class_info == Positioner and isinstance(obj, FakePositioner):
|
119
|
+
return True
|
120
|
+
return original_isinstance(obj, class_info)
|
77
121
|
|
78
|
-
|
122
|
+
with patch("builtins.isinstance", new=isinstance_mock):
|
123
|
+
yield client
|
tests/test_bec_figure.py
CHANGED
@@ -5,7 +5,8 @@ from unittest.mock import MagicMock
|
|
5
5
|
import numpy as np
|
6
6
|
import pytest
|
7
7
|
|
8
|
-
from bec_widgets.widgets import BECFigure
|
8
|
+
from bec_widgets.widgets import BECFigure, BECMotorMap, BECWaveform1D
|
9
|
+
from bec_widgets.widgets.plots import BECImageShow
|
9
10
|
|
10
11
|
from .client_mocks import mocked_client
|
11
12
|
|
@@ -66,6 +67,16 @@ def test_bec_figure_add_remove_plot(bec_figure):
|
|
66
67
|
assert bec_figure._widgets["widget_2"].config.widget_class == "BECWaveform1D"
|
67
68
|
|
68
69
|
|
70
|
+
def test_add_different_types_of_widgets(bec_figure):
|
71
|
+
plt = bec_figure.plot("samx", "bpm4i")
|
72
|
+
im = bec_figure.image("eiger")
|
73
|
+
motor_map = bec_figure.motor_map("samx", "samy")
|
74
|
+
|
75
|
+
assert plt.__class__ == BECWaveform1D
|
76
|
+
assert im.__class__ == BECImageShow
|
77
|
+
assert motor_map.__class__ == BECMotorMap
|
78
|
+
|
79
|
+
|
69
80
|
def test_access_widgets_access_errors(bec_figure):
|
70
81
|
bec_figure.add_plot(row=0, col=0)
|
71
82
|
|
tests/test_bec_monitor.py
CHANGED
@@ -7,6 +7,8 @@ import yaml
|
|
7
7
|
|
8
8
|
from bec_widgets.widgets import BECMonitor
|
9
9
|
|
10
|
+
from .client_mocks import mocked_client
|
11
|
+
|
10
12
|
|
11
13
|
def load_test_config(config_name):
|
12
14
|
"""Helper function to load config from yaml file."""
|
@@ -16,69 +18,6 @@ def load_test_config(config_name):
|
|
16
18
|
return config
|
17
19
|
|
18
20
|
|
19
|
-
class FakeDevice:
|
20
|
-
"""Fake minimal positioner class for testing."""
|
21
|
-
|
22
|
-
def __init__(self, name, enabled=True):
|
23
|
-
self.name = name
|
24
|
-
self.enabled = enabled
|
25
|
-
self.signals = {self.name: {"value": 1.0}}
|
26
|
-
self.description = {self.name: {"source": self.name}}
|
27
|
-
|
28
|
-
def __contains__(self, item):
|
29
|
-
return item == self.name
|
30
|
-
|
31
|
-
@property
|
32
|
-
def _hints(self):
|
33
|
-
return [self.name]
|
34
|
-
|
35
|
-
def set_value(self, fake_value: float = 1.0) -> None:
|
36
|
-
"""
|
37
|
-
Setup fake value for device readout
|
38
|
-
Args:
|
39
|
-
fake_value(float): Desired fake value
|
40
|
-
"""
|
41
|
-
self.signals[self.name]["value"] = fake_value
|
42
|
-
|
43
|
-
def describe(self) -> dict:
|
44
|
-
"""
|
45
|
-
Get the description of the device
|
46
|
-
Returns:
|
47
|
-
dict: Description of the device
|
48
|
-
"""
|
49
|
-
return self.description
|
50
|
-
|
51
|
-
|
52
|
-
def get_mocked_device(device_name: str):
|
53
|
-
"""
|
54
|
-
Helper function to mock the devices
|
55
|
-
Args:
|
56
|
-
device_name(str): Name of the device to mock
|
57
|
-
"""
|
58
|
-
return FakeDevice(name=device_name, enabled=True)
|
59
|
-
|
60
|
-
|
61
|
-
@pytest.fixture(scope="function")
|
62
|
-
def mocked_client():
|
63
|
-
# Create a dictionary of mocked devices
|
64
|
-
device_names = ["samx", "gauss_bpm", "gauss_adc1", "gauss_adc2", "gauss_adc3", "bpm4i"]
|
65
|
-
mocked_devices = {name: get_mocked_device(name) for name in device_names}
|
66
|
-
|
67
|
-
# Create a MagicMock object
|
68
|
-
client = MagicMock()
|
69
|
-
|
70
|
-
# Mock the device_manager.devices attribute
|
71
|
-
client.device_manager.devices = MagicMock()
|
72
|
-
client.device_manager.devices.__getitem__.side_effect = lambda x: mocked_devices.get(x)
|
73
|
-
client.device_manager.devices.__contains__.side_effect = lambda x: x in mocked_devices
|
74
|
-
|
75
|
-
# Set each device as an attribute of the mock
|
76
|
-
for name, device in mocked_devices.items():
|
77
|
-
setattr(client.device_manager.devices, name, device)
|
78
|
-
|
79
|
-
return client
|
80
|
-
|
81
|
-
|
82
21
|
@pytest.fixture(scope="function")
|
83
22
|
def monitor(bec_dispatcher, qtbot, mocked_client):
|
84
23
|
# client = MagicMock()
|
@@ -266,9 +205,6 @@ def test_on_scan_segment(monitor, config_name, msg, metadata, expected_data):
|
|
266
205
|
config = load_test_config(config_name)
|
267
206
|
monitor.on_config_update(config)
|
268
207
|
|
269
|
-
# Get hints
|
270
|
-
monitor.dev.__getitem__.side_effect = mock_getitem
|
271
|
-
|
272
208
|
# Mock scan_storage.find_scan_by_ID
|
273
209
|
mock_scan_data = MagicMock()
|
274
210
|
mock_scan_data.data = {
|
@@ -0,0 +1,125 @@
|
|
1
|
+
import pytest
|
2
|
+
|
3
|
+
from bec_widgets.widgets import BECMotorMap
|
4
|
+
from bec_widgets.widgets.plots.motor_map import MotorMapConfig
|
5
|
+
from bec_widgets.widgets.plots.waveform1d import Signal, SignalData
|
6
|
+
|
7
|
+
from .client_mocks import mocked_client
|
8
|
+
|
9
|
+
|
10
|
+
@pytest.fixture(scope="function")
|
11
|
+
def bec_motor_map(qtbot, mocked_client):
|
12
|
+
widget = BECMotorMap(client=mocked_client, gui_id="BECMotorMap_test")
|
13
|
+
# qtbot.addWidget(widget)
|
14
|
+
# qtbot.waitExposed(widget)
|
15
|
+
yield widget
|
16
|
+
|
17
|
+
|
18
|
+
def test_motor_map_init(bec_motor_map):
|
19
|
+
default_config = MotorMapConfig(widget_class="BECMotorMap", gui_id="BECMotorMap_test")
|
20
|
+
|
21
|
+
assert bec_motor_map.config == default_config
|
22
|
+
|
23
|
+
|
24
|
+
def test_motor_map_change_motors(bec_motor_map):
|
25
|
+
bec_motor_map.change_motors("samx", "samy")
|
26
|
+
|
27
|
+
assert bec_motor_map.config.signals.x == SignalData(name="samx", entry="samx", limits=[-10, 10])
|
28
|
+
assert bec_motor_map.config.signals.y == SignalData(name="samy", entry="samy", limits=[-5, 5])
|
29
|
+
|
30
|
+
|
31
|
+
def test_motor_map_get_limits(bec_motor_map):
|
32
|
+
expected_limits = {
|
33
|
+
"samx": [-10, 10],
|
34
|
+
"samy": [-5, 5],
|
35
|
+
}
|
36
|
+
|
37
|
+
for motor_name, expected_limit in expected_limits.items():
|
38
|
+
actual_limit = bec_motor_map._get_motor_limit(motor_name)
|
39
|
+
assert actual_limit == expected_limit
|
40
|
+
|
41
|
+
|
42
|
+
def test_motor_map_get_init_position(bec_motor_map):
|
43
|
+
bec_motor_map.set_precision(2)
|
44
|
+
|
45
|
+
motor_map_dev = bec_motor_map.client.device_manager.devices
|
46
|
+
|
47
|
+
expected_positions = {
|
48
|
+
("samx", "samx"): motor_map_dev["samx"].read()["samx"]["value"],
|
49
|
+
("samy", "samy"): motor_map_dev["samy"].read()["samy"]["value"],
|
50
|
+
("aptrx", "aptrx"): motor_map_dev["aptrx"].read()["aptrx"]["value"],
|
51
|
+
("aptry", "aptry"): motor_map_dev["aptry"].read()["aptry"]["value"],
|
52
|
+
}
|
53
|
+
|
54
|
+
for (motor_name, entry), expected_position in expected_positions.items():
|
55
|
+
actual_position = bec_motor_map._get_motor_init_position(motor_name, entry, 2)
|
56
|
+
assert actual_position == expected_position
|
57
|
+
|
58
|
+
|
59
|
+
def test_motor_movement_updates_position_and_database(bec_motor_map):
|
60
|
+
motor_map_dev = bec_motor_map.client.device_manager.devices
|
61
|
+
|
62
|
+
init_positions = {
|
63
|
+
"samx": [motor_map_dev["samx"].read()["samx"]["value"]],
|
64
|
+
"samy": [motor_map_dev["samy"].read()["samy"]["value"]],
|
65
|
+
}
|
66
|
+
|
67
|
+
bec_motor_map.change_motors("samx", "samy")
|
68
|
+
|
69
|
+
assert bec_motor_map.database_buffer["x"] == init_positions["samx"]
|
70
|
+
assert bec_motor_map.database_buffer["y"] == init_positions["samy"]
|
71
|
+
|
72
|
+
# Simulate motor movement for 'samx' only
|
73
|
+
new_position_samx = 4.0
|
74
|
+
bec_motor_map.on_device_readback({"signals": {"samx": {"value": new_position_samx}}})
|
75
|
+
|
76
|
+
init_positions["samx"].append(new_position_samx)
|
77
|
+
init_positions["samy"].append(init_positions["samy"][-1])
|
78
|
+
# Verify database update for 'samx'
|
79
|
+
assert bec_motor_map.database_buffer["x"] == init_positions["samx"]
|
80
|
+
|
81
|
+
# Verify 'samy' retains its last known position
|
82
|
+
assert bec_motor_map.database_buffer["y"] == init_positions["samy"]
|
83
|
+
|
84
|
+
|
85
|
+
def test_scatter_plot_rendering(bec_motor_map):
|
86
|
+
motor_map_dev = bec_motor_map.client.device_manager.devices
|
87
|
+
|
88
|
+
init_positions = {
|
89
|
+
"samx": [motor_map_dev["samx"].read()["samx"]["value"]],
|
90
|
+
"samy": [motor_map_dev["samy"].read()["samy"]["value"]],
|
91
|
+
}
|
92
|
+
|
93
|
+
bec_motor_map.change_motors("samx", "samy")
|
94
|
+
|
95
|
+
# Simulate motor movement for 'samx' only
|
96
|
+
new_position_samx = 4.0
|
97
|
+
bec_motor_map.on_device_readback({"signals": {"samx": {"value": new_position_samx}}})
|
98
|
+
bec_motor_map._update_plot()
|
99
|
+
|
100
|
+
# Get the scatter plot item
|
101
|
+
scatter_plot_item = bec_motor_map.plot_components["scatter"]
|
102
|
+
|
103
|
+
# Check the scatter plot item properties
|
104
|
+
assert len(scatter_plot_item.data) > 0, "Scatter plot data is empty"
|
105
|
+
x_data = scatter_plot_item.data["x"]
|
106
|
+
y_data = scatter_plot_item.data["y"]
|
107
|
+
assert x_data[-1] == new_position_samx, "Scatter plot X data not updated correctly"
|
108
|
+
assert (
|
109
|
+
y_data[-1] == init_positions["samy"][-1]
|
110
|
+
), "Scatter plot Y data should retain last known position"
|
111
|
+
|
112
|
+
|
113
|
+
def test_plot_visualization_consistency(bec_motor_map):
|
114
|
+
bec_motor_map.change_motors("samx", "samy")
|
115
|
+
# Simulate updating the plot with new data
|
116
|
+
bec_motor_map.on_device_readback({"signals": {"samx": {"value": 5}}})
|
117
|
+
bec_motor_map.on_device_readback({"signals": {"samy": {"value": 9}}})
|
118
|
+
bec_motor_map._update_plot()
|
119
|
+
|
120
|
+
scatter_plot_item = bec_motor_map.plot_components["scatter"]
|
121
|
+
|
122
|
+
# Check if the scatter plot reflects the new data correctly
|
123
|
+
assert (
|
124
|
+
scatter_plot_item.data["x"][-1] == 5 and scatter_plot_item.data["y"][-1] == 9
|
125
|
+
), "Plot not updated correctly with new data"
|
tests/test_config_dialog.py
CHANGED
@@ -8,6 +8,8 @@ from qtpy.QtWidgets import QTableWidgetItem, QTabWidget
|
|
8
8
|
|
9
9
|
from bec_widgets.widgets.monitor.config_dialog import ConfigDialog
|
10
10
|
|
11
|
+
from .client_mocks import mocked_client
|
12
|
+
|
11
13
|
|
12
14
|
def load_test_config(config_name):
|
13
15
|
"""Helper function to load config from yaml file."""
|
@@ -17,69 +19,6 @@ def load_test_config(config_name):
|
|
17
19
|
return config
|
18
20
|
|
19
21
|
|
20
|
-
class FakeDevice:
|
21
|
-
"""Fake minimal positioner class for testing."""
|
22
|
-
|
23
|
-
def __init__(self, name, enabled=True):
|
24
|
-
self.name = name
|
25
|
-
self.enabled = enabled
|
26
|
-
self.signals = {self.name: {"value": 1.0}}
|
27
|
-
self.description = {self.name: {"source": self.name}}
|
28
|
-
|
29
|
-
def __contains__(self, item):
|
30
|
-
return item == self.name
|
31
|
-
|
32
|
-
@property
|
33
|
-
def _hints(self):
|
34
|
-
return [self.name]
|
35
|
-
|
36
|
-
def set_value(self, fake_value: float = 1.0) -> None:
|
37
|
-
"""
|
38
|
-
Setup fake value for device readout
|
39
|
-
Args:
|
40
|
-
fake_value(float): Desired fake value
|
41
|
-
"""
|
42
|
-
self.signals[self.name]["value"] = fake_value
|
43
|
-
|
44
|
-
def describe(self) -> dict:
|
45
|
-
"""
|
46
|
-
Get the description of the device
|
47
|
-
Returns:
|
48
|
-
dict: Description of the device
|
49
|
-
"""
|
50
|
-
return self.description
|
51
|
-
|
52
|
-
|
53
|
-
def get_mocked_device(device_name: str):
|
54
|
-
"""
|
55
|
-
Helper function to mock the devices
|
56
|
-
Args:
|
57
|
-
device_name(str): Name of the device to mock
|
58
|
-
"""
|
59
|
-
return FakeDevice(name=device_name, enabled=True)
|
60
|
-
|
61
|
-
|
62
|
-
@pytest.fixture(scope="function")
|
63
|
-
def mocked_client():
|
64
|
-
# Create a dictionary of mocked devices
|
65
|
-
device_names = ["samx", "gauss_bpm", "gauss_adc1", "gauss_adc2", "gauss_adc3", "bpm4i"]
|
66
|
-
mocked_devices = {name: get_mocked_device(name) for name in device_names}
|
67
|
-
|
68
|
-
# Create a MagicMock object
|
69
|
-
client = MagicMock()
|
70
|
-
|
71
|
-
# Mock the device_manager.devices attribute
|
72
|
-
client.device_manager.devices = MagicMock()
|
73
|
-
client.device_manager.devices.__getitem__.side_effect = lambda x: mocked_devices.get(x)
|
74
|
-
client.device_manager.devices.__contains__.side_effect = lambda x: x in mocked_devices
|
75
|
-
|
76
|
-
# Set each device as an attribute of the mock
|
77
|
-
for name, device in mocked_devices.items():
|
78
|
-
setattr(client.device_manager.devices, name, device)
|
79
|
-
|
80
|
-
return client
|
81
|
-
|
82
|
-
|
83
22
|
@pytest.fixture(scope="function")
|
84
23
|
def config_dialog(qtbot, mocked_client):
|
85
24
|
client = mocked_client
|