This commit is contained in:
Vincent Hanewinkel 2025-08-14 23:20:36 +02:00
parent dc5269e595
commit 5602c20cb3
2 changed files with 45 additions and 36 deletions

View File

@ -1,4 +1,7 @@
import threading, queue, time, csv, os, statistics import os, sys, time, csv, statistics, threading, queue
scriptDir = os.path.dirname(os.path.realpath(__file__))
DEFAULT_OUTDIR = os.path.join(scriptDir, "logs")
class DeviceWorker: class DeviceWorker:
""" """
@ -8,10 +11,11 @@ class DeviceWorker:
- 1 Hz Logging (eine CSV-Zeile pro Sekunde) - 1 Hz Logging (eine CSV-Zeile pro Sekunde)
Wichtig: kontrolliert NICHT die Session! Wichtig: kontrolliert NICHT die Session!
""" """
def __init__(self, dev, outdir="./logs", filter_window_size=10, interval=0.1): def __init__(self, dev, outdir=DEFAULT_OUTDIR, filter_window_size=10, interval=0.1):
self.dev = dev self.dev = dev
self.serial = getattr(dev, "serial", "UNKNOWN") self.serial = getattr(dev, "serial", "UNKNOWN")
self.outdir = outdir self.outdir = outdir
self._log_path = None
self.filter_window_size = filter_window_size self.filter_window_size = filter_window_size
self.interval = interval self.interval = interval
@ -25,30 +29,27 @@ class DeviceWorker:
# ---- API ---- # ---- API ----
def start(self): def start(self):
print(f"[{self.serial}] Worker.start()")
if not self._reader_t.is_alive(): self._reader_t.start() if not self._reader_t.is_alive(): self._reader_t.start()
if not self._writer_t.is_alive(): self._writer_t.start() if not self._writer_t.is_alive(): self._writer_t.start()
self._cmdq.put(("start", None)) self._cmdq.put(("start", None))
def stop(self): def stop(self):
print(f"[{self.serial}] Worker.stop()")
self._cmdq.put(("stop", None)) self._cmdq.put(("stop", None))
def shutdown(self):
self.stop()
self._stop_evt.set()
def set_mode(self, ch, mode):
key = {0: "A", 1: "B"}.get(ch, str(ch).upper())
self.dev.channels[key].mode = mode
# ---- Threads ----
def _reader_loop(self): def _reader_loop(self):
print(f"[{self.serial}] Reader gestartet (interval={self.interval}, n={self.filter_window_size})")
last_log = 0.0 last_log = 0.0
while not self._stop_evt.is_set(): while not self._stop_evt.is_set():
# Kommandos
try: try:
cmd, _ = self._cmdq.get_nowait() cmd, _ = self._cmdq.get_nowait()
if cmd == "start": self._running = True if cmd == "start":
elif cmd == "stop": self._running = False self._running = True
print(f"[{self.serial}] RUNNING")
elif cmd == "stop":
self._running = False
print(f"[{self.serial}] STOPPED")
except queue.Empty: except queue.Empty:
pass pass
@ -57,9 +58,12 @@ class DeviceWorker:
continue continue
try: try:
# wie im Single-Logger: n Samples holen, mitteln # WÄHLE HIER DIE SIGNATUR, DIE DEIN SINGLE-LOGGER NUTZT:
# WICHTIG: kleiner Timeout, damit der Thread nie "fest" hängt # Beispiel A (deine Datei ließ 'read(n, 500, True)' vermuten):
samples = self.dev.read(self.filter_window_size, 50) # timeout ~50 ms samples = self.dev.read(self.filter_window_size, 500, True)
# Beispiel B (andere pysmu-Versionen):
# samples = self.dev.read(self.filter_window_size, -1)
if not samples: if not samples:
time.sleep(self.interval) time.sleep(self.interval)
continue continue
@ -68,34 +72,38 @@ class DeviceWorker:
vB = statistics.mean(row[2] for row in samples) vB = statistics.mean(row[2] for row in samples)
now = time.time() now = time.time()
if now - last_log >= 1.0: # 1 Hz ins File if now - last_log >= 1.0:
try: self._writer_q.put((now, vA, vB))
self._writer_q.put((now, vA, vB), timeout=0.2)
last_log = now last_log = now
except queue.Full:
pass
except Exception as e: except Exception as e:
print(f"[{self.serial}] Read-Fehler: {e}") print(f"[{self.serial}] Read-Fehler: {e}")
time.sleep(0.02) time.sleep(0.05)
# Loop-Takt exakt wie beim Single-Logger
time.sleep(max(0.05, self.interval)) time.sleep(max(0.05, self.interval))
def _writer_loop(self): def _writer_loop(self):
os.makedirs(self.outdir, exist_ok=True)
fn = os.path.join(self.outdir, f"{time.strftime('%Y%m%d_%H%M%S')}_{self.serial}.csv")
try: try:
with open(fn, "w", newline="") as f: os.makedirs(self.outdir, exist_ok=True)
except Exception as e:
print(f"[{self.serial}] os.makedirs-Fehler für '{self.outdir}': {e}")
return
self._log_path = os.path.join(self.outdir, f"{time.strftime('%Y%m%d_%H%M%S')}_{self.serial}.csv")
print(f"[{self.serial}] Writer startet → {self._log_path}")
try:
with open(self._log_path, "w", newline="") as f:
w = csv.writer(f) w = csv.writer(f)
w.writerow(["timestamp", "A", "B"]) w.writerow(["timestamp", "A", "B"]) # Datei wird JETZT angelegt
while not (self._stop_evt.is_set() and self._writer_q.empty()): while not (self._stop_evt.is_set() and self._writer_q.empty()):
try: try:
ts, vA, vB = self._writer_q.get(timeout=0.5) ts, vA, vB = self._writer_q.get(timeout=0.5)
except queue.Empty: except queue.Empty:
continue continue
w.writerow([ts, vA, vB]) w.writerow([ts, vA, vB])
f.flush() # bei 1 Hz ok f.flush()
except Exception as e: except Exception as e:
print(f"[{self.serial}] Writer-Fehler: {e}") print(f"[{self.serial}] Writer-Fehler ({self._log_path}): {e}")
finally: finally:
print(f"[{self.serial}] Datei geschlossen: {fn}") print(f"[{self.serial}] Writer beendet: {self._log_path}")

View File

@ -1,7 +1,10 @@
import sys import sys, os
from PyQt5.QtWidgets import QApplication, QWidget, QListWidget, QListWidgetItem, QVBoxLayout, QPushButton, QLabel, QHBoxLayout from PyQt5.QtWidgets import QApplication, QWidget, QListWidget, QListWidgetItem, QVBoxLayout, QPushButton, QLabel, QHBoxLayout
from multi_logger import MultiLogger from multi_logger import MultiLogger
scriptDir = os.path.dirname(os.path.realpath(__file__))
DEFAULT_OUTDIR = os.path.join(scriptDir, "logs")
class ListItemWidget(QWidget): class ListItemWidget(QWidget):
def __init__(self, label, worker): def __init__(self, label, worker):
super().__init__() super().__init__()
@ -48,11 +51,9 @@ class MainWindow(QWidget):
if __name__ == "__main__": if __name__ == "__main__":
# 1) Logger + Geräte # 1) Logger + Geräte
ml = MultiLogger(outdir="./logs", filter_window_size=10, interval=0.1) ml = MultiLogger(outdir=DEFAULT_OUTDIR, filter_window_size=10, interval=0.1)
print("Gefundene Geräte:", [w.serial for w in ml.workers]) print("Gefundene Geräte:", [w.serial for w in ml.workers])
ml.set_default_modes() ml.set_default_modes()
# 2) Session EINMAL im Hauptthread starten (wie beim Single-Logger!)
ml.start_session() ml.start_session()
# 3) GUI # 3) GUI