mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
refactor: add support to plot against x_data
This commit is contained in:
@ -1144,13 +1144,13 @@ class Waveform(PlotBase):
|
|||||||
to reduce the number of copies in between cycles. Be careful when refactoring
|
to reduce the number of copies in between cycles. Be careful when refactoring
|
||||||
this part as it will affect the performance of the async readback.
|
this part as it will affect the performance of the async readback.
|
||||||
|
|
||||||
|
Async curves support plotting against 'index' or other 'device_signal'. No 'auto' or 'timestamp'.
|
||||||
|
The fallback mechanism for 'auto' and 'timestamp' is to use the 'index'.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
We create data_plot_x and data_plot_y and modify them within this function
|
We create data_plot_x and data_plot_y and modify them within this function
|
||||||
to avoid creating new arrays. This is important for performance.
|
to avoid creating new arrays. This is important for performance.
|
||||||
We adjust the variables based on instruction type.
|
Support update instructions are 'add', 'add_slice', and 'replace'.
|
||||||
- add: Add the new data to the existing data.
|
|
||||||
- add_slice: Add the new data to the existing data and set the slice index.
|
|
||||||
- replace: Replace the existing data with the new data.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
msg(dict): Message with the async data.
|
msg(dict): Message with the async data.
|
||||||
@ -1196,14 +1196,31 @@ class Waveform(PlotBase):
|
|||||||
|
|
||||||
# Replace is trivial, no need to modify data_plot_y
|
# Replace is trivial, no need to modify data_plot_y
|
||||||
|
|
||||||
# Get the x data if 'timestamp' is selected, otherwise compute it
|
# Get x data for plotting
|
||||||
data_plot_x = async_data["timestamp"]
|
if plot_mode in ["index", "auto", "timestamp"]:
|
||||||
if plot_mode == "timestamp" and instruction != "add_slice":
|
|
||||||
if data_plot_x is not None and x_data is not None:
|
|
||||||
data_plot_x = np.hstack((x_data, data_plot_x))
|
|
||||||
else:
|
|
||||||
data_plot_x = np.linspace(0, len(data_plot_y) - 1, len(data_plot_y))
|
data_plot_x = np.linspace(0, len(data_plot_y) - 1, len(data_plot_y))
|
||||||
# Set the x data
|
self._auto_adjust_async_curve_settings(curve, len(data_plot_y))
|
||||||
|
curve.setData(data_plot_x, data_plot_y)
|
||||||
|
# Move on in the loop
|
||||||
|
continue
|
||||||
|
# Only consider device signals that are async for now, fallback is index
|
||||||
|
x_device_entry = self.x_axis_mode["entry"]
|
||||||
|
async_data = msg["signals"].get(x_device_entry, None)
|
||||||
|
# Make sure the signal exists, otherwise fall back to index
|
||||||
|
if async_data is None:
|
||||||
|
# Try to grab the data from device signals
|
||||||
|
data_plot_x = self._get_x_data(plot_mode, x_device_entry)
|
||||||
|
if len(data_plot_x) != len(data_plot_y):
|
||||||
|
# If the data is not the same length, fall back to index
|
||||||
|
logger.warning(
|
||||||
|
f"Async data for curve {curve.name()} is None. Falling back to index."
|
||||||
|
)
|
||||||
|
data_plot_x = np.linspace(0, len(data_plot_y) - 1, len(data_plot_y))
|
||||||
|
elif x_data is not None:
|
||||||
|
data_plot_x = np.hstack((x_data, async_data["value"]))
|
||||||
|
else:
|
||||||
|
data_plot_x = async_data["value"]
|
||||||
|
# Plot the data
|
||||||
self._auto_adjust_async_curve_settings(curve, len(data_plot_y))
|
self._auto_adjust_async_curve_settings(curve, len(data_plot_y))
|
||||||
curve.setData(data_plot_x, data_plot_y)
|
curve.setData(data_plot_x, data_plot_y)
|
||||||
|
|
||||||
|
@ -135,13 +135,13 @@ def test_async_plotting(qtbot, bec_client_lib, connected_client_gui_obj):
|
|||||||
|
|
||||||
# Wait for the scan to finish and the data to be available in history
|
# Wait for the scan to finish and the data to be available in history
|
||||||
# Wait until scan_id is in history
|
# Wait until scan_id is in history
|
||||||
def _wait_for_scan_in_hisotry():
|
def _wait_for_scan_in_history():
|
||||||
if len(client.history) == 0:
|
if len(client.history) == 0:
|
||||||
return False
|
return False
|
||||||
# Once items appear in storage, the last one hast to be the one we just scanned
|
# Once items appear in storage, the last one hast to be the one we just scanned
|
||||||
return client.history[-1].metadata.bec["scan_id"] == status.scan.scan_id
|
return client.history[-1].metadata.bec["scan_id"] == status.scan.scan_id
|
||||||
|
|
||||||
qtbot.waitUntil(_wait_for_scan_in_hisotry, timeout=10000)
|
qtbot.waitUntil(_wait_for_scan_in_history, timeout=10000)
|
||||||
last_scan_data = client.history[-1]
|
last_scan_data = client.history[-1]
|
||||||
# check plotted data
|
# check plotted data
|
||||||
x_data, y_data = curve.get_data()
|
x_data, y_data = curve.get_data()
|
||||||
|
@ -353,7 +353,7 @@ def test_update_async_curves(monkeypatch, qtbot, mocked_client):
|
|||||||
wf = create_widget(qtbot, Waveform, client=mocked_client)
|
wf = create_widget(qtbot, Waveform, client=mocked_client)
|
||||||
c = wf.plot(arg1="async_device", label="async_device-async_device")
|
c = wf.plot(arg1="async_device", label="async_device-async_device")
|
||||||
wf._async_curves = [c]
|
wf._async_curves = [c]
|
||||||
wf.x_mode = "timestamp"
|
wf.x_mode = "timestamp" # Timestamp is not supported, fallback to index.
|
||||||
dummy_scan = create_dummy_scan_item()
|
dummy_scan = create_dummy_scan_item()
|
||||||
wf.scan_item = dummy_scan
|
wf.scan_item = dummy_scan
|
||||||
|
|
||||||
@ -366,7 +366,7 @@ def test_update_async_curves(monkeypatch, qtbot, mocked_client):
|
|||||||
monkeypatch.setattr(c, "setData", fake_setData)
|
monkeypatch.setattr(c, "setData", fake_setData)
|
||||||
|
|
||||||
wf.update_async_curves()
|
wf.update_async_curves()
|
||||||
np.testing.assert_array_equal(recorded.get("x"), [11, 21, 31])
|
np.testing.assert_array_equal(recorded.get("x"), [0, 1, 2])
|
||||||
np.testing.assert_array_equal(recorded.get("y"), [1, 2, 3])
|
np.testing.assert_array_equal(recorded.get("y"), [1, 2, 3])
|
||||||
|
|
||||||
|
|
||||||
@ -528,12 +528,10 @@ def test_setup_async_curve(qtbot, mocked_client, monkeypatch):
|
|||||||
assert "async_device" in endpoint_called
|
assert "async_device" in endpoint_called
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("x_mode", ("timestamp", "index"))
|
def test_on_async_readback_add_update(qtbot, mocked_client):
|
||||||
def test_on_async_readback_add_update(qtbot, mocked_client, x_mode):
|
|
||||||
"""
|
"""
|
||||||
Test that on_async_readback extends or replaces async data depending on metadata instruction.
|
Test that on_async_readback extends or replaces async data depending on metadata instruction.
|
||||||
For 'timestamp' mode, new timestamps are appended to x_data.
|
'Index' mode
|
||||||
For 'index' mode, x_data simply increases by integer index.
|
|
||||||
"""
|
"""
|
||||||
wf = create_widget(qtbot, Waveform, client=mocked_client)
|
wf = create_widget(qtbot, Waveform, client=mocked_client)
|
||||||
wf.scan_item = create_dummy_scan_item()
|
wf.scan_item = create_dummy_scan_item()
|
||||||
@ -543,7 +541,7 @@ def test_on_async_readback_add_update(qtbot, mocked_client, x_mode):
|
|||||||
c.setData([0, 1, 2], [10, 11, 12])
|
c.setData([0, 1, 2], [10, 11, 12])
|
||||||
|
|
||||||
# Set the x_axis_mode
|
# Set the x_axis_mode
|
||||||
wf.x_axis_mode["name"] = x_mode
|
wf.x_axis_mode["name"] = "index"
|
||||||
|
|
||||||
############# Test add ################
|
############# Test add ################
|
||||||
|
|
||||||
@ -554,10 +552,7 @@ def test_on_async_readback_add_update(qtbot, mocked_client, x_mode):
|
|||||||
x_data, y_data = c.get_data()
|
x_data, y_data = c.get_data()
|
||||||
assert len(x_data) == 5
|
assert len(x_data) == 5
|
||||||
# Check x_data based on x_mode
|
# Check x_data based on x_mode
|
||||||
if x_mode == "timestamp":
|
np.testing.assert_array_equal(x_data, [0, 1, 2, 3, 4])
|
||||||
np.testing.assert_array_equal(x_data, [0, 1, 2, 1001, 1002])
|
|
||||||
else: # x_mode == "index"
|
|
||||||
np.testing.assert_array_equal(x_data, [0, 1, 2, 3, 4])
|
|
||||||
|
|
||||||
np.testing.assert_array_equal(y_data, [10, 11, 12, 100, 200])
|
np.testing.assert_array_equal(y_data, [10, 11, 12, 100, 200])
|
||||||
|
|
||||||
@ -566,11 +561,7 @@ def test_on_async_readback_add_update(qtbot, mocked_client, x_mode):
|
|||||||
metadata2 = {"async_update": {"max_shape": [None], "type": "replace"}}
|
metadata2 = {"async_update": {"max_shape": [None], "type": "replace"}}
|
||||||
wf.on_async_readback(msg2, metadata2)
|
wf.on_async_readback(msg2, metadata2)
|
||||||
x_data2, y_data2 = c.get_data()
|
x_data2, y_data2 = c.get_data()
|
||||||
if x_mode == "timestamp":
|
np.testing.assert_array_equal(x_data2, [0])
|
||||||
np.testing.assert_array_equal(x_data2, [555])
|
|
||||||
else:
|
|
||||||
|
|
||||||
np.testing.assert_array_equal(x_data2, [0])
|
|
||||||
|
|
||||||
np.testing.assert_array_equal(y_data2, [999])
|
np.testing.assert_array_equal(y_data2, [999])
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user