Adalm1000_Logger/DeviceController.py
Vincent Hanewinkel 7d9be883a5 fix
2025-08-14 22:15:32 +02:00

106 lines
3.9 KiB
Python

import threading, queue, time, csv, os
CHUNK = 2000
SAMPLE_RATE = 100_000.0 # 100 kS/s
DT = 1.0 / SAMPLE_RATE
OUTDIR = "logs"
FLUSH_EVERY = 50
os.makedirs(OUTDIR, exist_ok=True)
class DeviceController:
def __init__(self, sess, dev):
self.sess = sess # <— Session referenzieren
self.dev = dev
self.cmdq = queue.Queue()
self.stop_evt = threading.Event()
self.running = False
self.writer_q = queue.Queue(maxsize=50)
self.reader_t = threading.Thread(target=self.reader_loop, daemon=True)
self.writer_t = threading.Thread(target=self.writer_loop, daemon=True)
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))
def stop(self):
self.cmdq.put(("stop", None))
def set_mode(self, ch, mode):
# BUGFIX: nicht rekursiv sich selbst aufrufen!
self.dev.channels[ch].mode = mode
def reader_loop(self):
while not self.stop_evt.is_set():
try:
cmd, arg = self.cmdq.get_nowait()
if cmd == "start" and not self.running:
# Continuous Session-Stream starten (für alle Devices)
self.sess.start(0) # <— statt dev.run(0)
self.running = True
elif cmd == "stop" and self.running:
self.sess.end() # <— Session sauber beenden
# optional: Puffer leeren
# self.sess.flush(-1, True) # falls verfügbar in deiner Version
self.running = False
elif cmd == "mode":
ch, mode = arg
self.dev.channels[ch].mode = mode
except queue.Empty:
pass
if self.running:
try:
# Lies jeweils CHUNK Samples (blockierend bis voll)
data = self.dev.read(CHUNK, -1) # -> [[VA, IA, VB, IB], ...]
vsA = [row[0] for row in data]
vsB = [row[2] for row in data]
self.writer_q.put((time.time(), vsA, vsB))
except Exception:
time.sleep(0.001)
else:
time.sleep(0.01)
def writer_loop(self):
os.makedirs(OUTDIR, exist_ok=True)
ts0 = None
fn = os.path.join(OUTDIR, f"{time.strftime('%Y%m%d_%H%M%S')}_{self.dev.serial}.csv")
chunks_written = 0
sample_idx = 0
try:
with open(fn, "w", newline="") as f:
w = csv.writer(f)
# Long-Format: eine Zeile pro Kanalwert
w.writerow(["t_rel_s", "ch", "value"])
while not (self.stop_evt.is_set() and self.writer_q.empty()):
try:
ts, va, vb = self.writer_q.get(timeout=0.2)
except Exception:
continue
if ts0 is None:
ts0 = ts
# Sicherstellen, dass A und B gleich lang sind
if len(va) != len(vb):
n = min(len(va), len(vb))
va, vb = va[:n], vb[:n]
# Zeitindex aus Sample-Index ableiten (konstante Rate)
for i, (a, b) in enumerate(zip(va, vb)):
t_rel = (sample_idx + i) * DT
w.writerow([t_rel, "A", a])
w.writerow([t_rel, "B", b])
sample_idx += len(va)
chunks_written += 1
if chunks_written % FLUSH_EVERY == 0:
f.flush() # optional: os.fsync(f.fileno())
except Exception as e:
print(f"[{self.dev.serial}] Writer-Fehler: {e}")
finally:
print(f"[{self.dev.serial}] Datei geschlossen: {fn}")