bec-ipython-client 3.70.0__py3-none-any.whl → 3.86.1__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.

Potentially problematic release.


This version of bec-ipython-client might be problematic. Click here for more details.

@@ -7,34 +7,94 @@ from bec_lib import messages
7
7
  from bec_lib.queue_items import QueueItem
8
8
 
9
9
 
10
- @pytest.mark.timeout(20)
11
- def test_live_updates_process_queue_pending(bec_client_mock):
10
+ @pytest.fixture
11
+ def queue_elements(bec_client_mock):
12
12
  client = bec_client_mock
13
- live_updates = IPythonLiveUpdates(client)
14
13
  request_msg = messages.ScanQueueMessage(
15
14
  scan_type="grid_scan",
16
15
  parameter={"args": {"samx": (-5, 5, 3)}, "kwargs": {}},
17
16
  queue="primary",
18
17
  metadata={"RID": "something"},
19
18
  )
19
+ request_block = messages.RequestBlock(
20
+ msg=request_msg,
21
+ RID="req_id",
22
+ scan_motors=["samx"],
23
+ report_instructions=[],
24
+ readout_priority={"monitored": ["samx"]},
25
+ is_scan=True,
26
+ scan_number=1,
27
+ scan_id="scan_id",
28
+ )
20
29
  queue = QueueItem(
21
30
  scan_manager=client.queue,
22
31
  queue_id="queue_id",
23
- request_blocks=[request_msg],
32
+ request_blocks=[request_block],
24
33
  status="PENDING",
25
34
  active_request_block={},
26
35
  scan_id=["scan_id"],
27
36
  )
28
- client.queue.queue_storage.current_scan_queue = {"primary": {"status": "RUNNING"}}
37
+ return queue, request_block, request_msg
38
+
39
+
40
+ @pytest.fixture
41
+ def sample_request_msg():
42
+ return messages.ScanQueueMessage(
43
+ scan_type="grid_scan",
44
+ parameter={"args": {"samx": (-5, 5, 3)}, "kwargs": {}},
45
+ queue="primary",
46
+ metadata={"RID": "something"},
47
+ )
48
+
49
+
50
+ @pytest.fixture
51
+ def sample_request_block(sample_request_msg):
52
+ return messages.RequestBlock(
53
+ msg=sample_request_msg,
54
+ RID="req_id",
55
+ scan_motors=["samx"],
56
+ report_instructions=[],
57
+ readout_priority={"monitored": ["samx"]},
58
+ is_scan=True,
59
+ scan_number=1,
60
+ scan_id="scan_id",
61
+ )
62
+
63
+
64
+ @pytest.fixture
65
+ def sample_queue_info_entry(sample_request_block):
66
+ return messages.QueueInfoEntry(
67
+ queue_id="test_queue_id",
68
+ scan_id=["scan_id"],
69
+ is_scan=[True],
70
+ request_blocks=[sample_request_block],
71
+ scan_number=[1],
72
+ status="RUNNING",
73
+ active_request_block=None,
74
+ )
75
+
76
+
77
+ @pytest.fixture
78
+ def sample_scan_queue_status(sample_queue_info_entry):
79
+ return messages.ScanQueueStatus(info=[sample_queue_info_entry], status="RUNNING")
80
+
81
+
82
+ @pytest.mark.timeout(20)
83
+ def test_live_updates_process_queue_pending(bec_client_mock, queue_elements):
84
+ client = bec_client_mock
85
+ live_updates = IPythonLiveUpdates(client)
86
+ queue, request_block, request_msg = queue_elements
87
+
88
+ client.queue.queue_storage.current_scan_queue = {
89
+ "primary": messages.ScanQueueStatus(info=[], status="RUNNING")
90
+ }
29
91
  with mock.patch.object(queue, "_update_with_buffer"):
30
92
  with mock.patch(
31
93
  "bec_lib.queue_items.QueueItem.queue_position", new_callable=mock.PropertyMock
32
94
  ) as queue_pos:
33
95
  queue_pos.return_value = 2
34
96
  with mock.patch.object(
35
- live_updates,
36
- "_available_req_blocks",
37
- return_value=[{"report_instructions": [], "content": {"scan_type": "grid_scan"}}],
97
+ live_updates, "_available_req_blocks", return_value=[request_block]
38
98
  ):
39
99
  with mock.patch.object(live_updates, "_process_report_instructions") as process:
40
100
  with mock.patch("builtins.print") as prt:
@@ -45,39 +105,30 @@ def test_live_updates_process_queue_pending(bec_client_mock):
45
105
 
46
106
 
47
107
  @pytest.mark.timeout(20)
48
- def test_live_updates_process_queue_running(bec_client_mock):
108
+ def test_live_updates_process_queue_running(bec_client_mock, queue_elements):
49
109
  client = bec_client_mock
50
110
  live_updates = IPythonLiveUpdates(client)
51
- request_msg = messages.ScanQueueMessage(
52
- scan_type="grid_scan",
53
- parameter={"args": {"samx": (-5, 5, 3)}, "kwargs": {}},
54
- queue="primary",
55
- metadata={"RID": "something"},
56
- )
111
+ _, request_block, request_msg = queue_elements
57
112
  queue = QueueItem(
58
113
  scan_manager=client.queue,
59
114
  queue_id="queue_id",
60
- request_blocks=[request_msg],
115
+ request_blocks=[request_block],
61
116
  status="RUNNING",
62
117
  active_request_block={},
63
118
  scan_id=["scan_id"],
64
119
  )
65
120
  live_updates._active_request = request_msg
66
- client.queue.queue_storage.current_scan_queue = {"primary": {"status": "RUNNING"}}
121
+ request_block.report_instructions = [{"wait_table": 10}]
122
+ client.queue.queue_storage.current_scan_queue = {
123
+ "primary": messages.ScanQueueStatus(info=[], status="RUNNING")
124
+ }
67
125
  with mock.patch.object(queue, "_update_with_buffer"):
68
126
  with mock.patch(
69
127
  "bec_lib.queue_items.QueueItem.queue_position", new_callable=mock.PropertyMock
70
128
  ) as queue_pos:
71
129
  queue_pos.return_value = 2
72
130
  with mock.patch.object(
73
- live_updates,
74
- "_available_req_blocks",
75
- return_value=[
76
- {
77
- "report_instructions": [{"wait_table": 10}],
78
- "content": {"scan_type": "grid_scan"},
79
- }
80
- ],
131
+ live_updates, "_available_req_blocks", return_value=[request_block]
81
132
  ):
82
133
  with mock.patch.object(live_updates, "_process_instruction") as process:
83
134
  with mock.patch("builtins.print") as prt:
@@ -88,37 +139,19 @@ def test_live_updates_process_queue_running(bec_client_mock):
88
139
 
89
140
 
90
141
  @pytest.mark.timeout(20)
91
- def test_live_updates_process_queue_without_status(bec_client_mock):
142
+ def test_live_updates_process_queue_without_status(bec_client_mock, queue_elements):
92
143
  client = bec_client_mock
93
144
  live_updates = IPythonLiveUpdates(client)
94
- request_msg = messages.ScanQueueMessage(
95
- scan_type="grid_scan",
96
- parameter={"args": {"samx": (-5, 5, 3)}, "kwargs": {}},
97
- queue="primary",
98
- metadata={"RID": "something"},
99
- )
100
- queue = QueueItem(
101
- scan_manager=client.queue,
102
- queue_id="queue_id",
103
- request_blocks=[request_msg],
104
- status=None,
105
- active_request_block={},
106
- scan_id=["scan_id"],
107
- )
145
+ queue, _, request_msg = queue_elements
108
146
  with mock.patch.object(queue, "_update_with_buffer"):
109
147
  assert live_updates._process_queue(queue, request_msg, "req_id") is False
110
148
 
111
149
 
112
150
  @pytest.mark.timeout(20)
113
- def test_live_updates_process_queue_without_queue_number(bec_client_mock):
151
+ def test_live_updates_process_queue_without_queue_number(bec_client_mock, queue_elements):
114
152
  client = bec_client_mock
115
153
  live_updates = IPythonLiveUpdates(client)
116
- request_msg = messages.ScanQueueMessage(
117
- scan_type="grid_scan",
118
- parameter={"args": {"samx": (-5, 5, 3)}, "kwargs": {}},
119
- queue="primary",
120
- metadata={"RID": "something"},
121
- )
154
+ queue, _, request_msg = queue_elements
122
155
 
123
156
  with mock.patch(
124
157
  "bec_lib.queue_items.QueueItem.queue_position", new_callable=mock.PropertyMock
@@ -136,24 +169,182 @@ def test_live_updates_process_queue_without_queue_number(bec_client_mock):
136
169
  assert live_updates._process_queue(queue, request_msg, "req_id") is False
137
170
 
138
171
 
139
- # @pytest.mark.timeout(20)
140
- # @pytest.mark.asyncio
141
- # def test_live_updates_process_instruction_readback(bec_client_mock):
142
- # client = bec_client_mock
143
- # live_updates = IPythonLiveUpdates(client)
144
- # request_msg = messages.ScanQueueMessage(
145
- # scan_type="grid_scan",
146
- # parameter={"args": {"samx": (-5, 5, 3)}, "kwargs": {}},
147
- # queue="primary",
148
- # metadata={"RID": "something"},
149
- # )
150
- # live_updates._active_request = request_msg
151
- # live_updates._user_callback = []
152
- # client.queue.queue_storage.current_scan_queue = {"primary": {"status": "RUNNING"}}
153
- # with mock.patch(
154
- # "bec_client_mock.callbacks.ipython_live_updates.LiveUpdatesTable", new_callable=mock.Co
155
- # ) as table:
156
- # live_updates._process_instruction({"scan_progress": 10})
157
- # table.assert_called_once_with(
158
- # client, report_instructions={"scan_progress": 10}, request=request_msg, callbacks=[]
159
- # )
172
+ @pytest.mark.timeout(20)
173
+ def test_available_req_blocks(bec_client_mock, queue_elements):
174
+ client = bec_client_mock
175
+ live_updates = IPythonLiveUpdates(client)
176
+ queue, request_block, request_msg = queue_elements
177
+
178
+ # Test with matching RID
179
+ available_blocks = live_updates._available_req_blocks(queue, request_msg)
180
+ assert (
181
+ len(available_blocks) == 0
182
+ ) # request_block.RID is "req_id", request_msg.metadata["RID"] is "something"
183
+
184
+ # Test with correct RID
185
+ request_block.RID = "something"
186
+ available_blocks = live_updates._available_req_blocks(queue, request_msg)
187
+ assert len(available_blocks) == 1
188
+ assert available_blocks[0] == request_block
189
+
190
+
191
+ @pytest.mark.timeout(20)
192
+ def test_available_req_blocks_multiple_blocks(bec_client_mock):
193
+ client = bec_client_mock
194
+ live_updates = IPythonLiveUpdates(client)
195
+
196
+ request_msg = messages.ScanQueueMessage(
197
+ scan_type="grid_scan",
198
+ parameter={"args": {"samx": (-5, 5, 3)}, "kwargs": {}},
199
+ queue="primary",
200
+ metadata={"RID": "test_rid"},
201
+ )
202
+
203
+ request_block1 = messages.RequestBlock(
204
+ msg=request_msg,
205
+ RID="test_rid",
206
+ scan_motors=["samx"],
207
+ report_instructions=[],
208
+ readout_priority={"monitored": ["samx"]},
209
+ is_scan=True,
210
+ scan_number=1,
211
+ scan_id="scan_id_1",
212
+ )
213
+
214
+ request_block2 = messages.RequestBlock(
215
+ msg=request_msg,
216
+ RID="test_rid",
217
+ scan_motors=["samy"],
218
+ report_instructions=[],
219
+ readout_priority={"monitored": ["samy"]},
220
+ is_scan=True,
221
+ scan_number=2,
222
+ scan_id="scan_id_2",
223
+ )
224
+
225
+ request_block3 = messages.RequestBlock(
226
+ msg=request_msg,
227
+ RID="different_rid",
228
+ scan_motors=["samz"],
229
+ report_instructions=[],
230
+ readout_priority={"monitored": ["samz"]},
231
+ is_scan=True,
232
+ scan_number=3,
233
+ scan_id="scan_id_3",
234
+ )
235
+
236
+ queue = QueueItem(
237
+ scan_manager=client.queue,
238
+ queue_id="queue_id",
239
+ request_blocks=[request_block1, request_block2, request_block3],
240
+ status="RUNNING",
241
+ active_request_block={},
242
+ scan_id=["scan_id_1", "scan_id_2", "scan_id_3"],
243
+ )
244
+
245
+ available_blocks = live_updates._available_req_blocks(queue, request_msg)
246
+ assert len(available_blocks) == 2
247
+ assert request_block1 in available_blocks
248
+ assert request_block2 in available_blocks
249
+ assert request_block3 not in available_blocks
250
+
251
+
252
+ @pytest.mark.timeout(20)
253
+ def test_element_in_queue_no_queue(bec_client_mock):
254
+ client = bec_client_mock
255
+ live_updates = IPythonLiveUpdates(client)
256
+
257
+ # Test when client.queue is None
258
+ client.queue = None
259
+ assert live_updates._element_in_queue() is False
260
+
261
+
262
+ @pytest.mark.timeout(20)
263
+ def test_element_in_queue_no_current_scan_queue(bec_client_mock):
264
+ client = bec_client_mock
265
+ live_updates = IPythonLiveUpdates(client)
266
+
267
+ # Test when current_scan_queue is None
268
+ client.queue.queue_storage.current_scan_queue = None
269
+ assert live_updates._element_in_queue() is False
270
+
271
+
272
+ @pytest.mark.timeout(20)
273
+ def test_element_in_queue_no_primary_queue(bec_client_mock):
274
+ client = bec_client_mock
275
+ live_updates = IPythonLiveUpdates(client)
276
+
277
+ # Test when primary queue doesn't exist
278
+ scan_queue_status = messages.ScanQueueStatus(info=[], status="RUNNING")
279
+ client.queue.queue_storage.current_scan_queue = {"secondary": scan_queue_status}
280
+ assert live_updates._element_in_queue() is False
281
+
282
+
283
+ @pytest.mark.timeout(20)
284
+ def test_element_in_queue_no_queue_info(bec_client_mock):
285
+ client = bec_client_mock
286
+ live_updates = IPythonLiveUpdates(client)
287
+
288
+ # Test when queue_info is empty
289
+ scan_queue_status = messages.ScanQueueStatus(info=[], status="RUNNING")
290
+ client.queue.queue_storage.current_scan_queue = {"primary": scan_queue_status}
291
+ assert live_updates._element_in_queue() is False
292
+
293
+
294
+ @pytest.mark.timeout(20)
295
+ def test_element_in_queue_no_current_queue(bec_client_mock, sample_scan_queue_status):
296
+ client = bec_client_mock
297
+ live_updates = IPythonLiveUpdates(client)
298
+
299
+ # Test when _current_queue is None
300
+ live_updates._current_queue = None
301
+ client.queue.queue_storage.current_scan_queue = {"primary": sample_scan_queue_status}
302
+ assert live_updates._element_in_queue() is False
303
+
304
+
305
+ @pytest.mark.timeout(20)
306
+ def test_element_in_queue_queue_id_not_in_info(bec_client_mock, sample_request_block):
307
+ client = bec_client_mock
308
+ live_updates = IPythonLiveUpdates(client)
309
+
310
+ # Test when queue_id is not in info
311
+ current_queue = mock.MagicMock()
312
+ current_queue.queue_id = "my_queue_id"
313
+ live_updates._current_queue = current_queue
314
+
315
+ queue_info_entry = messages.QueueInfoEntry(
316
+ queue_id="different_queue_id",
317
+ scan_id=["scan_id"],
318
+ is_scan=[True],
319
+ request_blocks=[sample_request_block],
320
+ scan_number=[1],
321
+ status="RUNNING",
322
+ active_request_block=None,
323
+ )
324
+ scan_queue_status = messages.ScanQueueStatus(info=[queue_info_entry], status="RUNNING")
325
+ client.queue.queue_storage.current_scan_queue = {"primary": scan_queue_status}
326
+ assert live_updates._element_in_queue() is False
327
+
328
+
329
+ @pytest.mark.timeout(20)
330
+ def test_element_in_queue_queue_id_in_info(bec_client_mock, sample_request_block):
331
+ client = bec_client_mock
332
+ live_updates = IPythonLiveUpdates(client)
333
+
334
+ # Test when queue_id is in info (should return True)
335
+ current_queue = mock.MagicMock()
336
+ current_queue.queue_id = "my_queue_id"
337
+ live_updates._current_queue = current_queue
338
+
339
+ queue_info_entry = messages.QueueInfoEntry(
340
+ queue_id="my_queue_id",
341
+ scan_id=["scan_id"],
342
+ is_scan=[True],
343
+ request_blocks=[sample_request_block],
344
+ scan_number=[1],
345
+ status="RUNNING",
346
+ active_request_block=None,
347
+ )
348
+ scan_queue_status = messages.ScanQueueStatus(info=[queue_info_entry], status="RUNNING")
349
+ client.queue.queue_storage.current_scan_queue = {"primary": scan_queue_status}
350
+ assert live_updates._element_in_queue() is True
@@ -107,7 +107,6 @@ class TestLiveTable:
107
107
  )
108
108
  def test_get_devices_from_scan_data(self, bec_client_mock, request_msg, scan_report_devices):
109
109
  client = bec_client_mock
110
- client.start()
111
110
  data = messages.ScanMessage(
112
111
  point_id=0, scan_id="", data={}, metadata={"scan_report_devices": scan_report_devices}
113
112
  )
@@ -1,20 +1,23 @@
1
1
  import collections
2
+ import time
2
3
  from unittest import mock
3
4
 
4
5
  import pytest
5
6
 
6
7
  from bec_ipython_client.callbacks.move_device import (
7
8
  LiveUpdatesReadbackProgressbar,
8
- ReadbackDataMixin,
9
+ ReadbackDataHandler,
9
10
  )
10
11
  from bec_lib import messages
11
12
  from bec_lib.endpoints import MessageEndpoints
12
13
 
13
14
 
14
15
  @pytest.fixture
15
- def readback_data_mixin(bec_client_mock):
16
- with mock.patch.object(bec_client_mock.device_manager, "connector"):
17
- yield ReadbackDataMixin(bec_client_mock.device_manager, ["samx", "samy"])
16
+ def readback_data_handler(bec_client_mock, connected_connector):
17
+ with mock.patch.object(bec_client_mock.device_manager, "connector", connected_connector):
18
+ yield ReadbackDataHandler(
19
+ bec_client_mock.device_manager, ["samx", "samy"], request_id="something"
20
+ )
18
21
 
19
22
 
20
23
  def test_move_callback(bec_client_mock):
@@ -33,30 +36,25 @@ def test_move_callback(bec_client_mock):
33
36
  return readback[0]
34
37
 
35
38
  req_done = collections.deque()
36
- msg_acc = messages.DeviceReqStatusMessage(
37
- device="samx", success=True, metadata={"RID": "something"}
38
- )
39
- req_done.extend([[None], [None], [None], [msg_acc]])
39
+ req_done.extend([{"samx": (False, False)}, {"samx": (False, False)}, {"samx": (True, True)}])
40
40
 
41
41
  def mock_req_msg(*args):
42
42
  if len(req_done) > 1:
43
43
  return req_done.popleft()
44
44
  return req_done[0]
45
45
 
46
- with mock.patch("bec_ipython_client.callbacks.move_device.check_alarms") as check_alarms_mock:
47
- with mock.patch.object(ReadbackDataMixin, "wait_for_RID"):
48
- with mock.patch.object(LiveUpdatesReadbackProgressbar, "wait_for_request_acceptance"):
46
+ with mock.patch("bec_ipython_client.callbacks.move_device.check_alarms"):
47
+ with mock.patch.object(LiveUpdatesReadbackProgressbar, "wait_for_request_acceptance"):
48
+ with mock.patch.object(
49
+ LiveUpdatesReadbackProgressbar, "_print_client_msgs_asap"
50
+ ) as mock_client_msgs:
49
51
  with mock.patch.object(
50
- LiveUpdatesReadbackProgressbar, "_print_client_msgs_asap"
51
- ) as mock_client_msgs:
52
- with mock.patch.object(
53
- LiveUpdatesReadbackProgressbar, "_print_client_msgs_all"
54
- ) as mock_client_msgs_all:
55
- with mock.patch.object(
56
- ReadbackDataMixin, "get_device_values", mock_readback
57
- ):
52
+ LiveUpdatesReadbackProgressbar, "_print_client_msgs_all"
53
+ ) as mock_client_msgs_all:
54
+ with mock.patch.object(ReadbackDataHandler, "get_device_values", mock_readback):
55
+ with mock.patch.object(ReadbackDataHandler, "device_states", mock_req_msg):
58
56
  with mock.patch.object(
59
- ReadbackDataMixin, "get_request_done_msgs", mock_req_msg
57
+ ReadbackDataHandler, "done", side_effect=[False, False, True]
60
58
  ):
61
59
  LiveUpdatesReadbackProgressbar(bec=client, request=request).run()
62
60
  assert mock_client_msgs.called is True
@@ -82,28 +80,21 @@ def test_move_callback_with_report_instruction(bec_client_mock):
82
80
  return readback[0]
83
81
 
84
82
  req_done = collections.deque()
85
- msg_acc = messages.DeviceReqStatusMessage(
86
- device="samx", success=True, metadata={"RID": "something"}
87
- )
88
- req_done.extend([[None], [None], [None], [msg_acc]])
83
+ req_done.extend([{"samx": (False, False)}, {"samx": (False, False)}, {"samx": (True, True)}])
89
84
 
90
85
  def mock_req_msg(*args):
91
86
  if len(req_done) > 1:
92
87
  return req_done.popleft()
93
88
  return req_done[0]
94
89
 
95
- with mock.patch("bec_ipython_client.callbacks.move_device.check_alarms") as check_alarms_mock:
96
- with mock.patch.object(ReadbackDataMixin, "wait_for_RID"):
97
- with mock.patch.object(LiveUpdatesReadbackProgressbar, "wait_for_request_acceptance"):
98
- with mock.patch.object(LiveUpdatesReadbackProgressbar, "_print_client_msgs_asap"):
99
- with mock.patch.object(
100
- LiveUpdatesReadbackProgressbar, "_print_client_msgs_all"
101
- ):
102
- with mock.patch.object(
103
- ReadbackDataMixin, "get_device_values", mock_readback
104
- ):
90
+ with mock.patch("bec_ipython_client.callbacks.move_device.check_alarms"):
91
+ with mock.patch.object(LiveUpdatesReadbackProgressbar, "wait_for_request_acceptance"):
92
+ with mock.patch.object(LiveUpdatesReadbackProgressbar, "_print_client_msgs_asap"):
93
+ with mock.patch.object(LiveUpdatesReadbackProgressbar, "_print_client_msgs_all"):
94
+ with mock.patch.object(ReadbackDataHandler, "get_device_values", mock_readback):
95
+ with mock.patch.object(ReadbackDataHandler, "device_states", mock_req_msg):
105
96
  with mock.patch.object(
106
- ReadbackDataMixin, "get_request_done_msgs", mock_req_msg
97
+ ReadbackDataHandler, "done", side_effect=[False, False, False, True]
107
98
  ):
108
99
  LiveUpdatesReadbackProgressbar(
109
100
  bec=client,
@@ -112,70 +103,121 @@ def test_move_callback_with_report_instruction(bec_client_mock):
112
103
  ).run()
113
104
 
114
105
 
115
- def test_readback_data_mixin(readback_data_mixin):
116
- readback_data_mixin.device_manager.connector.get.side_effect = [
117
- messages.DeviceMessage(
106
+ def test_readback_data_handler(readback_data_handler):
107
+ readback_data_handler.data = {
108
+ "samx": messages.DeviceMessage(
118
109
  signals={"samx": {"value": 10}, "samx_setpoint": {"value": 20}},
119
110
  metadata={"device": "samx"},
120
111
  ),
121
- messages.DeviceMessage(
112
+ "samy": messages.DeviceMessage(
122
113
  signals={"samy": {"value": 10}, "samy_setpoint": {"value": 20}},
123
114
  metadata={"device": "samy"},
124
115
  ),
125
- ]
126
- res = readback_data_mixin.get_device_values()
116
+ }
117
+
118
+ res = readback_data_handler.get_device_values()
127
119
  assert res == [10, 10]
128
120
 
129
121
 
130
- def test_readback_data_mixin_multiple_hints(readback_data_mixin):
131
- readback_data_mixin.device_manager.devices.samx._info["hints"]["fields"] = [
122
+ def test_readback_data_handler_multiple_hints(readback_data_handler):
123
+ readback_data_handler.device_manager.devices.samx._info["hints"]["fields"] = [
132
124
  "samx_setpoint",
133
125
  "samx",
134
126
  ]
135
- readback_data_mixin.device_manager.connector.get.side_effect = [
136
- messages.DeviceMessage(
127
+ readback_data_handler.data = {
128
+ "samx": messages.DeviceMessage(
137
129
  signals={"samx": {"value": 10}, "samx_setpoint": {"value": 20}},
138
130
  metadata={"device": "samx"},
139
131
  ),
140
- messages.DeviceMessage(
132
+ "samy": messages.DeviceMessage(
141
133
  signals={"samy": {"value": 10}, "samy_setpoint": {"value": 20}},
142
134
  metadata={"device": "samy"},
143
135
  ),
144
- ]
145
- res = readback_data_mixin.get_device_values()
136
+ }
137
+ res = readback_data_handler.get_device_values()
146
138
  assert res == [20, 10]
147
139
 
148
140
 
149
- def test_readback_data_mixin_multiple_no_hints(readback_data_mixin):
150
- readback_data_mixin.device_manager.devices.samx._info["hints"]["fields"] = []
151
- readback_data_mixin.device_manager.connector.get.side_effect = [
152
- messages.DeviceMessage(
141
+ def test_readback_data_handler_multiple_no_hints(readback_data_handler):
142
+ readback_data_handler.device_manager.devices.samx._info["hints"]["fields"] = []
143
+ readback_data_handler.data = {
144
+ "samx": messages.DeviceMessage(
153
145
  signals={"samx": {"value": 10}, "samx_setpoint": {"value": 20}},
154
146
  metadata={"device": "samx"},
155
147
  ),
156
- messages.DeviceMessage(
148
+ "samy": messages.DeviceMessage(
157
149
  signals={"samy": {"value": 10}, "samy_setpoint": {"value": 20}},
158
150
  metadata={"device": "samy"},
159
151
  ),
160
- ]
161
- res = readback_data_mixin.get_device_values()
152
+ }
153
+ res = readback_data_handler.get_device_values()
162
154
  assert res == [10, 10]
163
155
 
164
156
 
165
- def test_get_request_done_msgs(readback_data_mixin):
166
- res = readback_data_mixin.get_request_done_msgs()
167
- readback_data_mixin.device_manager.connector.pipeline.assert_called_once()
168
- assert (
169
- mock.call(
170
- MessageEndpoints.device_req_status("samx"),
171
- readback_data_mixin.device_manager.connector.pipeline.return_value,
172
- )
173
- in readback_data_mixin.device_manager.connector.get.call_args_list
157
+ def test_readback_data_handler_init(readback_data_handler):
158
+ """
159
+ Test that the ReadbackDataHandler is initialized correctly.
160
+ """
161
+
162
+ # Initial state
163
+ assert readback_data_handler._devices_done_state == {
164
+ "samx": (False, False),
165
+ "samy": (False, False),
166
+ }
167
+ assert readback_data_handler._devices_received == {"samx": False, "samy": False}
168
+ assert readback_data_handler.data == {}
169
+
170
+
171
+ def test_readback_data_handler_readback_callbacks(readback_data_handler):
172
+ """
173
+ Test that the readback callback properly updates the readback data.
174
+ """
175
+
176
+ # Submit readback for samx
177
+ msg = messages.DeviceMessage(
178
+ signals={"samx": {"value": 15}}, metadata={"device": "samx", "RID": "something"}
174
179
  )
175
- assert (
176
- mock.call(
177
- MessageEndpoints.device_req_status("samy"),
178
- readback_data_mixin.device_manager.connector.pipeline.return_value,
179
- )
180
- in readback_data_mixin.device_manager.connector.get.call_args_list
180
+ readback_data_handler.connector.set_and_publish(MessageEndpoints.device_readback("samx"), msg)
181
+
182
+ msg_old = messages.DeviceMessage(
183
+ signals={"samy": {"value": 10}}, metadata={"device": "samx", "RID": "something_else"}
184
+ )
185
+ readback_data_handler.connector.set_and_publish(
186
+ MessageEndpoints.device_readback("samy"), msg_old
187
+ )
188
+ while (
189
+ not readback_data_handler._devices_received["samx"]
190
+ or "samx" not in readback_data_handler.data
191
+ ):
192
+ time.sleep(0.01)
193
+ assert readback_data_handler.data["samx"].signals["samx"]["value"] == 15
194
+ dev_data = readback_data_handler.get_device_values()
195
+ assert dev_data[0] == 15
196
+ assert dev_data[1] == 10 # samy remains unchanged
197
+
198
+
199
+ def test_readback_data_handler_request_done_callbacks(readback_data_handler):
200
+ """
201
+ Test that the request done callback properly updates the device done state.
202
+ """
203
+
204
+ # Submit request done for samx
205
+ msg = messages.DeviceReqStatusMessage(device="samx", success=True, request_id="something")
206
+ readback_data_handler.connector.xadd(
207
+ MessageEndpoints.device_req_status("something"), {"data": msg}
208
+ )
209
+ while not readback_data_handler._devices_done_state["samx"][0]:
210
+ time.sleep(0.01)
211
+ assert readback_data_handler._devices_done_state["samx"] == (True, True)
212
+
213
+ assert readback_data_handler.done() is False
214
+
215
+ # Submit request done for samy
216
+ msg = messages.DeviceReqStatusMessage(device="samy", success=False, request_id="something")
217
+ readback_data_handler.connector.xadd(
218
+ MessageEndpoints.device_req_status("something"), {"data": msg}
181
219
  )
220
+ while not readback_data_handler._devices_done_state["samy"][0]:
221
+ time.sleep(0.01)
222
+ assert readback_data_handler._devices_done_state["samy"] == (True, False)
223
+ assert readback_data_handler.done() is True