MainCode/adalm1000_logger.py aktualisiert

(D)
This commit is contained in:
Jan 2025-06-28 01:19:18 +02:00
parent a5ad053a0c
commit f779d97397

View File

@ -27,57 +27,83 @@ class DeviceDisconnectedError(Exception):
pass pass
class MeasurementThread(QThread): class MeasurementThread(QThread):
update_signal = pyqtSignal(float, float, float) update_signal = pyqtSignal(float, float, float) # voltage, current, timestamp
error_signal = pyqtSignal(str) error_signal = pyqtSignal(str)
status_signal = pyqtSignal(str) # New: for status updates
def __init__(self, device, interval=0.1): def __init__(self, device, interval=0.1):
super().__init__() super().__init__()
self.device = device self.device = device
self.interval = interval self.interval = max(0.05, interval) # Ensure minimum interval
self._running = False self._running = False
self._lock = threading.Lock() # Thread safety
self.filter_window_size = 10 self.filter_window_size = 10
self.start_time = time.time() self.start_time = time.time()
self.last_update_time = self.start_time
def run(self): def run(self):
"""Main measurement loop with enhanced error handling"""
self._running = True self._running = True
voltage_window = [] voltage_window = deque(maxlen=self.filter_window_size)
current_window = [] current_window = deque(maxlen=self.filter_window_size)
self.status_signal.emit("Measurement started")
while self._running: while self._running:
try: try:
samples = self.device.read(self.filter_window_size, 500, True) # 1. Read samples with timeout
samples = self.device.read(
self.filter_window_size,
timeout=500,
)
if not samples: if not samples:
raise DeviceDisconnectedError("Keine Samples empfangen") raise DeviceDisconnectedError("No samples received - device may be disconnected")
raw_voltage = np.mean([s[1][0] for s in samples]) # 2. Process samples (thread-safe)
raw_current = np.mean([s[0][1] for s in samples]) with self._lock:
current_time = time.time() - self.start_time raw_voltage = np.mean([s[1][0] for s in samples])
raw_current = np.mean([s[0][1] for s in samples])
# Gleitender Mittelwertfilter
voltage_window.append(raw_voltage) voltage_window.append(raw_voltage)
current_window.append(raw_current) current_window.append(raw_current)
if len(voltage_window) > self.filter_window_size: voltage = np.mean(voltage_window)
voltage_window.pop(0) current = np.mean(current_window)
current_window.pop(0) current_time = time.time() - self.start_time
voltage = np.mean(voltage_window)
current = np.mean(current_window)
# 3. Emit updates
self.update_signal.emit(voltage, current, current_time) self.update_signal.emit(voltage, current, current_time)
time.sleep(max(0.05, self.interval)) self.last_update_time = time.time()
# 4. Dynamic sleep adjustment
elapsed = time.time() - self.last_update_time
sleep_time = max(0.01, self.interval - elapsed)
time.sleep(sleep_time)
except DeviceDisconnectedError as e:
self.error_signal.emit(f"Device error: {str(e)}")
break
except Exception as e: except Exception as e:
self.error_signal.emit(str(e)) self.error_signal.emit(f"Measurement error: {str(e)}")
self.status_signal.emit(f"Error: {str(e)}")
break break
self.status_signal.emit("Measurement stopped")
self._running = False
def stop(self): def stop(self):
"""Safe thread termination with timeout"""
self._running = False self._running = False
if self.isRunning(): if self.isRunning():
self.quit() self.quit()
if not self.wait(500): # Wait up to 500ms for clean exit if not self.wait(300): # 300ms grace period
print("Warning: Thread didn't exit cleanly, terminating") self.terminate() # Forceful termination if needed
self.terminate()
def is_active(self):
"""Check if thread is running and updating"""
with self._lock:
return self._running and (time.time() - self.last_update_time < 2.0)
class BatteryTester(QMainWindow): class BatteryTester(QMainWindow):
def __init__(self): def __init__(self):
@ -720,30 +746,59 @@ class BatteryTester(QMainWindow):
QTimer.singleShot(100, self.finalize_test) QTimer.singleShot(100, self.finalize_test)
def finalize_test(self): def finalize_test(self):
"""Finale Bereinigung nach Testende""" """Finale Bereinigung nach Testende mit verbessertem Fenster-Handling"""
# Protokolldaten schreiben try:
if hasattr(self, 'log_buffer') and self.log_buffer: # 1. Protokolldaten schreiben (mit zusätzlichem Lock)
try: if hasattr(self, 'log_buffer') and self.log_buffer:
self.log_writer.writerows(self.log_buffer) try:
self.log_buffer.clear() with threading.Lock(): # Thread-sicherer Zugriff
except Exception as e: self.log_writer.writerows(self.log_buffer)
print(f"Fehler beim Schreiben des Protokollpuffers: {e}") self.log_buffer.clear()
except Exception as e:
# Protokolldatei schließen print(f"Fehler beim Schreiben des Protokollpuffers: {e}")
if hasattr(self, 'current_cycle_file'):
try: # 2. Protokolldatei schließen (mit Fehlertoleranz)
self.current_cycle_file.close() if hasattr(self, 'current_cycle_file') and self.current_cycle_file:
except Exception as e: try:
print(f"Fehler beim Schließen der Protokolldatei: {e}") self.current_cycle_file.flush() # Daten sicher schreiben
os.fsync(self.current_cycle_file.fileno()) # Betriebssystem-Puffer leeren
# Benachrichtigung anzeigen self.current_cycle_file.close()
QMessageBox.information( except Exception as e:
self, print(f"Fehler beim Schließen der Protokolldatei: {e}")
"Test abgeschlossen",
f"Test wurde sicher beendet.\n\n" # 3. Benachrichtigung anzeigen (mit Fokus-Sicherung)
f"Entladekapazität: {self.capacity_ah:.3f}Ah\n" msg_box = QMessageBox(self) # Expliziter Parent
f"Abgeschlossene Zyklen: {self.cycle_count}" msg_box.setWindowFlags(msg_box.windowFlags() |
) Qt.WindowStaysOnTopHint | # Immer im Vordergrund
Qt.MSWindowsFixedSizeDialogHint) # Besseres Verhalten unter Windows
msg_box.setIcon(QMessageBox.Information)
msg_box.setWindowTitle("Test abgeschlossen")
msg_box.setText(
f"Test wurde sicher beendet.\n\n"
f"Entladekapazität: {self.capacity_ah:.3f}Ah\n"
f"Abgeschlossene Zyklen: {self.cycle_count}"
)
# Sicherstellen, dass das Fenster sichtbar wird
msg_box.raise_()
msg_box.activateWindow()
# Non-blocking anzeigen und Fokus erzwingen
QTimer.singleShot(100, lambda: (
msg_box.show(),
msg_box.raise_(),
msg_box.activateWindow()
))
msg_box.exec_()
except Exception as e:
print(f"Kritischer Fehler in finalize_test: {e}")
finally:
# 4. Gerätestatus zurücksetzen
self.test_running = False
self.request_stop = True
self.measuring = False
def update_measurements(self, voltage, current, current_time): def update_measurements(self, voltage, current, current_time):
"""Aktualisiert die Messwerte im UI""" """Aktualisiert die Messwerte im UI"""
@ -833,7 +888,7 @@ class BatteryTester(QMainWindow):
voltage_padding = 0.2 voltage_padding = 0.2
min_voltage = max(0, float(self.discharge_cutoff_input.text()) - voltage_padding) min_voltage = max(0, float(self.discharge_cutoff_input.text()) - voltage_padding)
max_voltage = float(self.charge_cutoff_input.text()) + voltage_padding max_voltage = float(self.charge_cutoff_input.text()) + voltage_padding
self.ax.set_ylim(min_voltage, max_volume) self.ax.set_ylim(min_voltage, max_voltage)
self.canvas.draw() self.canvas.draw()