Files
templates/python/set_devices/tektronix_session.py

80 lines
3.2 KiB
Python

"""Qt-independent capture queue, preview and full-record CSV export."""
from dataclasses import dataclass
from queue import Queue
from threading import Thread
import csv
from .tektronix import DPO4034, Waveform
from .plot_decimation import pixel_envelope
@dataclass
class CaptureFrame:
full: list
preview: list
def prepare_preview(waveforms, width):
preview = []
for wave in waveforms:
if not wave.seconds:
preview.append(wave)
continue
points = [point for point in pixel_envelope(wave.seconds, wave.values,
wave.seconds[0], wave.seconds[-1], width) if point is not None]
preview.append(Waveform(wave.channel, tuple(x for x, _ in points),
tuple(y for _, y in points), wave.unit))
return preview
class ScopeSession:
"""Serial worker queue: every VISA call, including close, stays off the UI."""
def __init__(self, factory=DPO4034):
self.commands = Queue()
self.results = Queue()
self.thread = Thread(target=self._run, args=(factory,))
self.thread.start()
def _run(self, factory):
client = factory()
try:
while True:
action, args = self.commands.get()
if action == "shutdown":
break
try:
if action == "capture" and len(args) == 4:
value = client.capture(*args[:3])
value = CaptureFrame(value, prepare_preview(value, args[3]))
elif action == "export":
filename, waves = args
with open(filename, "w", newline="", encoding="utf-8-sig") as stream:
writer = csv.writer(stream)
# Native channels share the frozen record's time grid.
# Reject mismatched grids rather than misalign exported values.
if any(w.seconds != waves[0].seconds for w in waves[1:]):
raise ValueError("Для экспорта каналов нужны одинаковые временные сетки")
writer.writerow(["time_ms", *(w.channel + " (" + w.unit + ")" for w in waves)])
for index, stamp in enumerate(waves[0].seconds):
writer.writerow([stamp * 1000, *(w.values[index] for w in waves)])
value = filename
else:
value = getattr(client, action)(*args)
self.results.put((action, value, None))
except Exception as exc:
if action != "export":
try:
client.close()
except Exception:
pass
self.results.put((action, None, str(exc)))
finally:
try:
client.close()
except Exception:
pass # Shutdown must finish even if the device has been unplugged.
def submit(self, action, *args):
self.commands.put((action, args))