0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-13 19:21:50 +02:00

refactor: improve plotting behaviour from history

This commit is contained in:
2025-04-08 10:02:11 +02:00
committed by wyzula_j
parent 25820a1cde
commit ed2d958de6

View File

@ -1098,17 +1098,20 @@ class Waveform(PlotBase):
if len(np.shape(device_data)) > 1:
device_data = device_data[-1, :]
x_data = self._get_x_data(device_name, device_entry)
if device_data is None:
logger.warning(f"Async data for curve {curve.name()} is None.")
continue
# Async curves only support plotting vs index or other device
if self.x_axis_mode["name"] in ["timestamp", "index", "auto"]:
device_data_x = np.linspace(0, len(device_data) - 1, len(device_data))
else:
# Fetch data from signal instead
device_data_x = self._get_x_data(device_name, device_entry)
self._auto_adjust_async_curve_settings(curve, len(device_data))
curve.setData(device_data_x, device_data)
# If there's actual data, set it
if device_data is not None:
self._auto_adjust_async_curve_settings(curve, len(device_data))
if x_data is not None or self.x_axis_mode["name"] == "timestamp":
curve.setData(x_data, device_data)
else:
curve.setData(
np.linspace(0, len(device_data) - 1, len(device_data)), device_data
)
self.request_dap_update.emit()
def _setup_async_curve(self, curve: Curve):
@ -1137,69 +1140,72 @@ class Waveform(PlotBase):
@SafeSlot(dict, dict)
def on_async_readback(self, msg, metadata):
"""
Get async data readback.
Get async data readback. This code needs to be fast, therefor we try
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.
Note:
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.
We adjust the variables based on instruction type.
- 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:
msg(dict): Message with the async data.
metadata(dict): Metadata of the message.
"""
instruction = metadata.get("async_update", {}).get("type")
if instruction not in ["add", "add_slice", "replace"]:
logger.warning(f"Invalid async update instruction: {instruction}")
return
max_shape = metadata.get("async_update", {}).get("max_shape", [])
plot_mode = self.x_axis_mode["name"]
for curve in self._async_curves:
new_data = None
y_data = None
x_data = None
x_name = self.x_axis_mode["name"]
x_data = None # Reset x_data
# Get the curve data
async_data = msg["signals"].get(curve.config.signal.entry, None)
if async_data is None:
continue
data_plot = async_data["value"]
# y-data
data_plot_y = async_data["value"]
if data_plot_y is None:
logger.warning(f"Async data for curve {curve.name()} is None.")
continue
# Add
if instruction == "add":
if len(max_shape) > 1:
if len(data_plot.shape) > 1:
data_plot = data_plot[-1, :]
# Ensure that data_plot_y is numpy array, this avoids copying if data_plot_y is already a numpy array
data_plot_y = np.asarray(data_plot_y)
if len(data_plot_y.shape) > 1:
data_plot_y = data_plot_y[-1, :]
else:
x_data, y_data = curve.get_data()
if y_data is not None:
new_data = np.hstack((y_data, data_plot)) # TODO check performance
else:
new_data = data_plot
if x_name == "timestamp":
if x_data is not None:
x_data = np.hstack((x_data, async_data["timestamp"]))
else:
x_data = async_data["timestamp"]
# FIXME x axis wrong if timestamp switched during scan
if y_data is not None:
data_plot_y = np.hstack((y_data, data_plot_y))
# Add slice
elif instruction == "add_slice":
if instruction == "add_slice":
current_slice_id = metadata.get("async_update", {}).get("index")
data_plot = async_data["value"]
if current_slice_id != curve.slice_index:
curve.slice_index = current_slice_id
else:
x_data, y_data = curve.get_data()
if y_data is not None:
new_data = np.hstack((y_data, data_plot))
else:
new_data = data_plot
# Replace
elif instruction == "replace":
if x_name == "timestamp":
x_data = async_data["timestamp"]
new_data = data_plot
if y_data is not None:
data_plot_y = np.hstack((y_data, data_plot_y))
# If update is not add, add_slice or replace, continue.
if new_data is None:
continue
# Hide symbol, activate downsampling if data >1000
self._auto_adjust_async_curve_settings(curve, len(new_data))
# Set data on the curve
if x_name == "timestamp" and instruction != "add_slice":
curve.setData(x_data, new_data)
# Replace is trivial, no need to modify data_plot_y
# Get the x data if 'timestamp' is selected, otherwise compute it
data_plot_x = async_data["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:
curve.setData(np.linspace(0, len(new_data) - 1, len(new_data)), new_data)
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)
self.request_dap_update.emit()