diff --git a/python/set_devices/qt_ports/address_history.py b/python/set_devices/qt_ports/address_history.py new file mode 100644 index 0000000..e9884fd --- /dev/null +++ b/python/set_devices/qt_ports/address_history.py @@ -0,0 +1,56 @@ +"""Editable recent-address selector with portable, per-interface JSON storage.""" +import json +from pathlib import Path +try: + from PySide6.QtCore import QSignalBlocker + from PySide6.QtWidgets import QComboBox, QSizePolicy +except ImportError: + from PySide2.QtCore import QSignalBlocker + from PySide2.QtWidgets import QComboBox, QSizePolicy + +class AddressHistoryBox(QComboBox): + def __init__(self, filename, parent=None): + super().__init__(parent) + self.filename = Path(filename) + self.interface = 'Ethernet' + self.history = {} + self.setEditable(True) + self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) + self.setInsertPolicy(QComboBox.InsertPolicy.NoInsert) + try: + saved = json.loads(self.filename.read_text(encoding='utf8')) + if isinstance(saved, dict): + self.history = {key: [x for x in items if isinstance(x, str) and x.strip()][:20] + for key, items in saved.items() if isinstance(items, list)} + except (OSError, ValueError): + pass + self.set_interface(self.interface) + + def text(self): + return self.currentText() + + def setText(self, text): + self.setEditText(text) + + def setPlaceholderText(self, text): + self.lineEdit().setPlaceholderText(text) + + def set_interface(self, interface): + self.interface = interface + with QSignalBlocker(self): + self.clear() + self.addItems(self.history.get(interface, [])) + if not self.count(): + self.setEditText('') + + def remember(self): + address = self.currentText().strip() + if not address: + return + previous = self.history.get(self.interface, []) + self.history[self.interface] = [address] + [x for x in previous if x.casefold()!=address.casefold()][:19] + self.filename.parent.mkdir(parents=True, exist_ok=True) + temporary = self.filename.with_suffix('.tmp') + temporary.write_text(json.dumps(self.history, ensure_ascii=False, indent=2), encoding='utf8') + temporary.replace(self.filename) + self.set_interface(self.interface) diff --git a/python/set_devices/tektronix.py b/python/set_devices/tektronix.py index 0278c8c..9263c6f 100644 --- a/python/set_devices/tektronix.py +++ b/python/set_devices/tektronix.py @@ -97,6 +97,19 @@ class DPO4034: return True raise ValueError("Некорректное состояние сбора: " + state) + def acquisition_status(self): + if self.instrument is None: + raise RuntimeError("Прибор не подключён") + fields = self.instrument.query("ACQUIRE:STATE?;:ACQUIRE:STOPAFTER?;:ACQUIRE:NUMACQ?").strip().split(";") + if len(fields) != 3: + raise ValueError("Некорректное состояние сбора") + state, mode, count = (value.strip().upper() for value in fields) + if state not in ("0", "1", "OFF", "ON", "STOP", "RUN"): + raise ValueError("Некорректное состояние сбора") + if mode not in ("RUNSTOP", "RUNST", "SEQUENCE", "SEQ"): + raise ValueError("Некорректный режим сбора") + return state in ("1", "ON", "RUN"), mode in ("SEQUENCE", "SEQ"), int(count) + def settings(self, channel="CH1"): self._channel(channel) if self.instrument is None: diff --git a/python/set_devices/tektronix_capture_state.py b/python/set_devices/tektronix_capture_state.py new file mode 100644 index 0000000..8809db0 --- /dev/null +++ b/python/set_devices/tektronix_capture_state.py @@ -0,0 +1,24 @@ +"""Read completed Single records once, without interfering with acquisition.""" +class CaptureState: + def __init__(self): + self.armed = False + self.delivered = None + + def arm(self): + self.armed = True + + def ready(self, status): + running, single, count = status + if not single: + self.armed = False + self.delivered = None + return running + if running or count == 0: + self.armed = True + return False + return count > 0 and (self.armed or self.delivered != count) + + def consumed(self, status): + if status[1]: + self.delivered = status[2] + self.armed = False diff --git a/python/set_devices/tektronix_session.py b/python/set_devices/tektronix_session.py index 537f295..b08f2b2 100644 --- a/python/set_devices/tektronix_session.py +++ b/python/set_devices/tektronix_session.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from queue import Queue from threading import Thread import csv +from .tektronix_capture_state import CaptureState from .tektronix import DPO4034, Waveform from .plot_decimation import pixel_envelope @@ -36,6 +37,7 @@ class ScopeSession: def _run(self, factory): client = factory() + state = CaptureState() try: while True: action, args = self.commands.get() @@ -44,11 +46,14 @@ class ScopeSession: try: if action == "capture" and len(args) in (4, 5): automatic = len(args) == 5 and args[4] - if automatic and not client.is_running(): + status = client.acquisition_status() if automatic else None + if automatic and not state.ready(status): value = None else: value = client.capture(*args[:3]) value = CaptureFrame(value, prepare_preview(value, args[3])) + if automatic: + state.consumed(status) elif action == "export": filename, waves = args with open(filename, "w", newline="", encoding="utf-8-sig") as stream: @@ -62,6 +67,10 @@ class ScopeSession: writer.writerow([stamp * 1000, *(w.values[index] for w in waves)]) value = filename else: + if action == "connect": + state = CaptureState() + if action == "acquisition" and args[0] == "single": + state.arm() value = getattr(client, action)(*args) self.results.put((action, value, None)) except Exception as exc: