fix
This commit is contained in:
parent
2cde913975
commit
8ea1532650
34
Controll.py
34
Controll.py
@ -1,18 +1,36 @@
|
||||
import sys
|
||||
import sys, pysmu
|
||||
from functools import partial
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from GUI import MainWindow
|
||||
from SessionManager import SessionManager
|
||||
from DeviceController import DeviceController
|
||||
from GUI import MainWindow
|
||||
|
||||
def main():
|
||||
# 1) Session + Devices
|
||||
sess = pysmu.Session()
|
||||
sess.add_all()
|
||||
sm = SessionManager(sess)
|
||||
|
||||
devices = list(sess.devices)
|
||||
print("Gefundene Geräte:", [d.serial for d in devices])
|
||||
|
||||
controllers = [DeviceController(sm, dev) for dev in devices]
|
||||
|
||||
# Optional: Modi setzen (A/B)
|
||||
for c in controllers:
|
||||
c.set_mode("A", pysmu.Mode.SIMV)
|
||||
c.set_mode("B", pysmu.Mode.HI_Z)
|
||||
|
||||
# 2) GUI
|
||||
app = QApplication(sys.argv)
|
||||
win = MainWindow()
|
||||
|
||||
ctrl = DeviceController(serial="TEST123")
|
||||
widget = win.add_list_item("TEST123", 0)
|
||||
|
||||
# 'checked' wird von Qt geliefert; wir fangen es in der Lambda ab
|
||||
widget.btn_start.clicked.connect(lambda checked=False: (ctrl.start(), widget.set_running(True)))
|
||||
widget.btn_stop.clicked.connect (lambda checked=False: (ctrl.stop(), widget.set_running(False)))
|
||||
# 3) Liste + Buttons
|
||||
for i, ctrl in enumerate(controllers):
|
||||
w = win.add_list_item(ctrl.serial, i)
|
||||
# 'checked' wird in der Lambda abgefangen, wir übergeben nur (ctrl, w)
|
||||
w.btn_start.clicked.connect(lambda checked=False, c=ctrl, ww=w: (c.start(), ww.set_running(True)))
|
||||
w.btn_stop.clicked.connect (lambda checked=False, c=ctrl, ww=w: (c.stop(), ww.set_running(False)))
|
||||
|
||||
win.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
@ -6,45 +6,56 @@ DT = 1.0 / SAMPLE_RATE
|
||||
CHUNK = 500 # 50 ms pro Chunk
|
||||
OUTDIR = "./logs"
|
||||
|
||||
def _norm_channel(ch):
|
||||
return {0:"A", 1:"B"}.get(ch, str(ch).upper())
|
||||
|
||||
class DeviceController:
|
||||
def __init__(self, serial="DUMMY"):
|
||||
self.serial = serial
|
||||
def __init__(self, session_manager, dev):
|
||||
self.sm = session_manager # <— SessionManager (teilt die Session)
|
||||
self.dev = dev
|
||||
self.serial = getattr(dev, "serial", "UNKNOWN")
|
||||
|
||||
self.cmdq = queue.Queue()
|
||||
self.writer_q = queue.Queue(maxsize=100)
|
||||
self.writer_q = queue.Queue(maxsize=200)
|
||||
self.stop_evt = threading.Event()
|
||||
self.running = False
|
||||
|
||||
self.reader_t = threading.Thread(target=self.reader_loop, daemon=True)
|
||||
self.writer_t = threading.Thread(target=self.writer_loop, daemon=True)
|
||||
|
||||
# === öffentliche API (nicht blockierend) ===
|
||||
# ==== Public API (nicht blockierend) ====
|
||||
def start(self):
|
||||
if not self.reader_t.is_alive(): self.reader_t.start()
|
||||
if not self.writer_t.is_alive(): self.writer_t.start()
|
||||
self.cmdq.put(("start", None))
|
||||
self.cmdq.put(("start", None)) # nur anstoßen
|
||||
|
||||
def stop(self):
|
||||
self.cmdq.put(("stop", None))
|
||||
self.cmdq.put(("stop", None)) # nur anstoßen
|
||||
|
||||
def shutdown(self):
|
||||
# Für Programmende
|
||||
self.stop()
|
||||
self.stop_evt.set()
|
||||
# nicht joinen aus dem GUI-Thread; nur beim Programmende joinen!
|
||||
|
||||
# === Dummy-Reader: erzeugt Daten, statt pysmu zu lesen ===
|
||||
def set_mode(self, ch, mode):
|
||||
key = _norm_channel(ch)
|
||||
self.dev.channels[key].mode = mode
|
||||
|
||||
# ==== Threads ====
|
||||
def reader_loop(self):
|
||||
phase = 0.0
|
||||
step = 2*math.pi * 50.0 * DT # 50 Hz Sinus
|
||||
print(f"[{self.serial}] Reader gestartet")
|
||||
while not self.stop_evt.is_set():
|
||||
# Kommandos abarbeiten
|
||||
# Kommandos (Start/Stop) abarbeiten
|
||||
try:
|
||||
cmd, arg = self.cmdq.get_nowait()
|
||||
if cmd == "start":
|
||||
if cmd == "start" and not self.running:
|
||||
n = self.sm.start() # evtl. startet hier die Session
|
||||
self.running = True
|
||||
print("[DUMMY] start")
|
||||
elif cmd == "stop":
|
||||
print(f"[{self.serial}] RUNNING (Session-Clients={n})")
|
||||
elif cmd == "stop" and self.running:
|
||||
n = self.sm.stop() # evtl. endet hier die Session
|
||||
self.running = False
|
||||
print("[DUMMY] stop")
|
||||
print(f"[{self.serial}] STOPPED (Session-Clients={n})")
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
||||
@ -52,32 +63,39 @@ class DeviceController:
|
||||
time.sleep(0.02)
|
||||
continue
|
||||
|
||||
# CHUNK Samples erzeugen
|
||||
va, vb = [], []
|
||||
for _ in range(CHUNK):
|
||||
a = math.sin(phase) # Kanal A
|
||||
b = 0.5*math.sin(phase*0.5) # Kanal B
|
||||
va.append(a)
|
||||
vb.append(b)
|
||||
phase += step
|
||||
|
||||
# ——— Daten lesen (blockierend, aber im Worker-Thread) ———
|
||||
try:
|
||||
self.writer_q.put((time.time(), va, vb), timeout=0.5)
|
||||
except queue.Full:
|
||||
print("[WARN] writer_q voll – Datenverlust")
|
||||
# kurze Pause, um CPU zu schonen
|
||||
time.sleep(CHUNK * DT)
|
||||
# pysmu: read liefert Liste von [VA, IA, VB, IB]
|
||||
samples = self.dev.read(CHUNK, -1) # je nach Version evtl. self.dev.read(CHUNK)
|
||||
if not samples:
|
||||
time.sleep(0.001)
|
||||
continue
|
||||
|
||||
va = [row[0] for row in samples]
|
||||
vb = [row[2] for row in samples]
|
||||
|
||||
# in Writer-Queue schieben
|
||||
try:
|
||||
self.writer_q.put((time.time(), va, vb), timeout=0.5)
|
||||
except queue.Full:
|
||||
print(f"[{self.serial}] WARN: writer_q voll – dropping chunk")
|
||||
except Exception as e:
|
||||
# NICHT schlucken – sichtbar loggen!
|
||||
print(f"[{self.serial}] Read-Fehler: {e}")
|
||||
time.sleep(0.01)
|
||||
|
||||
print(f"[{self.serial}] Reader beendet")
|
||||
|
||||
# === Writer: robust, Wide-Format ===
|
||||
def writer_loop(self):
|
||||
os.makedirs(OUTDIR, exist_ok=True)
|
||||
fn = os.path.join(OUTDIR, f"{time.strftime('%Y%m%d_%H%M%S')}_{self.serial}.csv")
|
||||
sample_idx = 0
|
||||
written_rows = 0
|
||||
print(f"[{self.serial}] Writer schreibt nach: {fn}")
|
||||
try:
|
||||
with open(fn, "w", newline="") as f:
|
||||
w = csv.writer(f)
|
||||
w.writerow(["t_rel_s", "A", "B"]) # Wide-Format
|
||||
w.writerow(["t_rel_s", "A", "B"]) # Wide-Format
|
||||
while not (self.stop_evt.is_set() and self.writer_q.empty()):
|
||||
try:
|
||||
ts, va, vb = self.writer_q.get(timeout=0.5)
|
||||
@ -91,11 +109,10 @@ class DeviceController:
|
||||
sample_idx += n
|
||||
written_rows += n
|
||||
|
||||
# damit man live sieht, dass wirklich geschrieben wird
|
||||
if written_rows and written_rows % (10*CHUNK) == 0:
|
||||
f.flush()
|
||||
print(f"[{self.serial}] geschrieben: {written_rows} Zeilen")
|
||||
print(f"[{self.serial}] rows={written_rows}")
|
||||
except Exception as e:
|
||||
print(f"[{self.serial}] Writer-Fehler: {e}")
|
||||
finally:
|
||||
print(f"[{self.serial}] Datei geschlossen: {fn}")
|
||||
print(f"[{self.serial}] Writer beendet: {fn}")
|
||||
28
SessionManager.py
Normal file
28
SessionManager.py
Normal file
@ -0,0 +1,28 @@
|
||||
import threading
|
||||
|
||||
class SessionManager:
|
||||
def __init__(self, sess):
|
||||
self.sess = sess
|
||||
self._lock = threading.Lock()
|
||||
self._running_count = 0 # wie viele Devices wollen gerade streamen?
|
||||
|
||||
def start(self):
|
||||
with self._lock:
|
||||
if self._running_count == 0:
|
||||
# Erster „Kunde“ → Session-Stream starten
|
||||
self.sess.start(0) # 0 = endlos
|
||||
self._running_count += 1
|
||||
return self._running_count
|
||||
|
||||
def stop(self):
|
||||
with self._lock:
|
||||
if self._running_count > 0:
|
||||
self._running_count -= 1
|
||||
if self._running_count == 0:
|
||||
# Letzter „Kunde“ ist weg → Session beenden
|
||||
self.sess.end()
|
||||
return self._running_count
|
||||
|
||||
def running_devices(self):
|
||||
with self._lock:
|
||||
return self._running_count
|
||||
Loading…
x
Reference in New Issue
Block a user