Track completed Single acquisitions and persist recent addresses
This commit is contained in:
56
python/set_devices/qt_ports/address_history.py
Normal file
56
python/set_devices/qt_ports/address_history.py
Normal file
@@ -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)
|
||||
@@ -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:
|
||||
|
||||
24
python/set_devices/tektronix_capture_state.py
Normal file
24
python/set_devices/tektronix_capture_state.py
Normal file
@@ -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
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user