MainCode/adalm1000_logger.py aktualisiert
One Device works the it crashes D
This commit is contained in:
parent
c2f91999c2
commit
d3d8ad2c5d
@ -17,23 +17,6 @@ from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QH
|
|||||||
from PyQt5.QtCore import Qt, QTimer, pyqtSignal, pyqtSlot, QObject, QThread
|
from PyQt5.QtCore import Qt, QTimer, pyqtSignal, pyqtSlot, QObject, QThread
|
||||||
from PyQt5 import sip
|
from PyQt5 import sip
|
||||||
import pysmu
|
import pysmu
|
||||||
import pysmu.libsmu
|
|
||||||
from pysmu.exceptions import SessionError
|
|
||||||
|
|
||||||
class PatchedSession(pysmu.Session):
|
|
||||||
def __init__(self, ignore_dataflow=False, queue_size=0):
|
|
||||||
try:
|
|
||||||
super().__init__(ignore_dataflow=ignore_dataflow, queue_size=queue_size)
|
|
||||||
except SessionError as e:
|
|
||||||
if "failed scanning" in str(e):
|
|
||||||
print("Warning: Session initialization quirk, continuing...")
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
finally: # Ensure scan happens regardless of exception
|
|
||||||
self.scan() # Move scan into finally block
|
|
||||||
|
|
||||||
if not self.devices:
|
|
||||||
print("No devices found after manual scan")
|
|
||||||
|
|
||||||
class DeviceManager:
|
class DeviceManager:
|
||||||
def __init__(self, dev):
|
def __init__(self, dev):
|
||||||
@ -70,11 +53,31 @@ class DeviceManager:
|
|||||||
'time': [], 'voltage': [], 'current': [], 'count': 0, 'last_plot_time': 0
|
'time': [], 'voltage': [], 'current': [], 'count': 0, 'last_plot_time': 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.consecutive_read_errors = 0 # Track read failures
|
||||||
|
self.max_consecutive_errors = 5 # Threshold before reset
|
||||||
|
|
||||||
|
def handle_read_error(self, increment=1):
|
||||||
|
"""Handle read errors with automatic reset"""
|
||||||
|
self.consecutive_read_errors += increment
|
||||||
|
|
||||||
|
if self.consecutive_read_errors >= self.max_consecutive_errors:
|
||||||
|
try:
|
||||||
|
print("Resetting device due to persistent errors")
|
||||||
|
self.dev.session.end()
|
||||||
|
time.sleep(0.5)
|
||||||
|
self.dev.session = PatchedSession()
|
||||||
|
self.dev.session.start(0)
|
||||||
|
return True # Recovery attempted
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Device reset failed: {e}")
|
||||||
|
return False # Unrecoverable error
|
||||||
|
return True # Under threshold - continue
|
||||||
|
|
||||||
def start_measurement(self, interval=0.1):
|
def start_measurement(self, interval=0.1):
|
||||||
if self.measurement_thread and self.measurement_thread.isRunning():
|
if self.measurement_thread and self.measurement_thread.isRunning():
|
||||||
self.measurement_thread.stop()
|
self.measurement_thread.stop()
|
||||||
|
|
||||||
self.measurement_thread = MeasurementThread(self.dev, interval)
|
self.measurement_thread = MeasurementThread(self.dev, interval, self) # Pass self as parent_manager
|
||||||
self.measurement_thread.start()
|
self.measurement_thread.start()
|
||||||
self.is_running = True
|
self.is_running = True
|
||||||
|
|
||||||
@ -109,7 +112,7 @@ class MeasurementThread(QThread):
|
|||||||
update_signal = pyqtSignal(float, float, float)
|
update_signal = pyqtSignal(float, float, float)
|
||||||
error_signal = pyqtSignal(str)
|
error_signal = pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, device, interval=0.1):
|
def __init__(self, device, interval, parent_manager): # Add parent_manager
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.device = device
|
self.device = device
|
||||||
self.interval = interval
|
self.interval = interval
|
||||||
@ -120,47 +123,35 @@ class MeasurementThread(QThread):
|
|||||||
self.start_time = None
|
self.start_time = None
|
||||||
self.measurement_queue = Queue(maxsize=1)
|
self.measurement_queue = Queue(maxsize=1)
|
||||||
self.current_direction = 1 # 1 for source, -1 for sink
|
self.current_direction = 1 # 1 for source, -1 for sink
|
||||||
|
self.parent_manager = parent_manager
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Continuous measurement loop"""
|
"""Continuous measurement loop with enhanced error handling"""
|
||||||
self._running = True
|
self._running = True
|
||||||
if self.start_time is None:
|
self.start_time = time.time()
|
||||||
self.start_time = time.time()
|
consecutive_errors = 0
|
||||||
|
max_consecutive_errors = 5
|
||||||
|
|
||||||
while self._running:
|
while self._running:
|
||||||
try:
|
try:
|
||||||
samples = self.device.read(self.filter_window_size, 500, True)
|
samples = self.device.read(self.filter_window_size, 500, True)
|
||||||
|
|
||||||
# --- VOLLSTÄNDIGER DEBUG-REPLACEMENT AB HIER ---
|
# --- Handle empty samples ---
|
||||||
print(f"\n{'='*50}")
|
|
||||||
print(f"DEBUG: Neue Messung - Zeit: {time.time():.3f}")
|
|
||||||
print(f"DEBUG: Anzahl empfangener Samples: {len(samples) if samples else 'None'}")
|
|
||||||
|
|
||||||
if not samples:
|
if not samples:
|
||||||
print("DEBUG: FEHLER - Keine Samples empfangen!")
|
consecutive_errors += 1
|
||||||
raise DeviceDisconnectedError("No samples received")
|
if consecutive_errors >= max_consecutive_errors:
|
||||||
|
# Attempt device reset through parent manager
|
||||||
# Zeige die komplette Struktur der ersten 3 Samples
|
if hasattr(self, 'parent_manager'):
|
||||||
for i, sample in enumerate(samples[:3]):
|
if not self.parent_manager.handle_read_error():
|
||||||
print(f"DEBUG: Sample[{i}] = {sample}")
|
raise DeviceDisconnectedError("Persistent read failures")
|
||||||
if len(sample) >= 2:
|
consecutive_errors = 0 # Reset after handling
|
||||||
print(f" Channel A: Strom={sample[0][1]:.6f}A, Spannung={sample[0][0]:.6f}V")
|
time.sleep(0.1)
|
||||||
print(f" Channel B: Strom={sample[1][1]:.6f}A, Spannung={sample[1][0]:.6f}V")
|
continue
|
||||||
|
|
||||||
# Berechne raw_voltage und zeige Zwischenergebnisse
|
# Reset error counter on successful read
|
||||||
voltage_values = [s[1][0] for s in samples]
|
consecutive_errors = 0
|
||||||
raw_voltage = np.mean(voltage_values)
|
|
||||||
print(f"DEBUG: Channel B Spannung - Werte: {voltage_values}, Mittelwert: {raw_voltage:.6f}V")
|
|
||||||
|
|
||||||
# Berechne raw_current und zeige Zwischenergebnisse
|
|
||||||
current_values = [s[0][1] for s in samples]
|
|
||||||
raw_current = np.mean(current_values) * self.current_direction
|
|
||||||
print(f"DEBUG: Channel A Strom - Werte: {current_values}, Mittelwert: {np.mean(current_values):.6f}A, mit Richtung: {raw_current:.6f}A")
|
|
||||||
print(f"{'='*50}")
|
|
||||||
# --- DEBUG-ENDE ---
|
|
||||||
|
|
||||||
if not samples:
|
|
||||||
raise DeviceDisconnectedError("No samples received")
|
|
||||||
|
|
||||||
|
# --- Process samples ---
|
||||||
current_time = time.time() - self.start_time
|
current_time = time.time() - self.start_time
|
||||||
|
|
||||||
# Get voltage from Channel B (HI_Z mode) and current from Channel A
|
# Get voltage from Channel B (HI_Z mode) and current from Channel A
|
||||||
@ -178,11 +169,11 @@ class MeasurementThread(QThread):
|
|||||||
voltage = np.mean(self.voltage_window)
|
voltage = np.mean(self.voltage_window)
|
||||||
current = np.mean(self.current_window)
|
current = np.mean(self.current_window)
|
||||||
|
|
||||||
# Validate measurements
|
# Validate measurements before processing
|
||||||
if voltage is None or not (-1.0 <= voltage <= 6.0):
|
if not (0.0 <= voltage <= 5.0): # ADALM1000 voltage range
|
||||||
raise ValueError(f"Invalid voltage: {voltage}V")
|
raise ValueError(f"Voltage out of range: {voltage:.4f}V")
|
||||||
if not (-0.25 <= current <= 0.25):
|
if not (-0.25 <= current <= 0.25): # ADALM1000 current range
|
||||||
raise ValueError(f"Invalid current: {current}A")
|
raise ValueError(f"Current out of range: {current:.4f}A")
|
||||||
|
|
||||||
# Emit update
|
# Emit update
|
||||||
self.update_signal.emit(voltage, current, current_time)
|
self.update_signal.emit(voltage, current, current_time)
|
||||||
@ -191,14 +182,32 @@ class MeasurementThread(QThread):
|
|||||||
try:
|
try:
|
||||||
self.measurement_queue.put_nowait((voltage, current))
|
self.measurement_queue.put_nowait((voltage, current))
|
||||||
except Full:
|
except Full:
|
||||||
pass
|
pass # It's OK to skip if queue is full
|
||||||
|
|
||||||
|
# Adaptive sleep based on interval
|
||||||
time.sleep(max(0.05, self.interval))
|
time.sleep(max(0.05, self.interval))
|
||||||
|
|
||||||
|
except DeviceDisconnectedError as e:
|
||||||
|
self.error_signal.emit(f"Device disconnected: {str(e)}")
|
||||||
|
break
|
||||||
|
except ValueError as e:
|
||||||
|
# Skip invalid measurements but log first occurrence
|
||||||
|
if consecutive_errors == 0:
|
||||||
|
self.error_signal.emit(f"Measurement error: {str(e)}")
|
||||||
|
consecutive_errors += 1
|
||||||
|
time.sleep(0.1)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.error_signal.emit(f"Read error: {str(e)}")
|
self.error_signal.emit(f"Read error: {str(e)}")
|
||||||
|
consecutive_errors += 1
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
continue
|
|
||||||
|
# Handle persistent errors
|
||||||
|
if consecutive_errors >= max_consecutive_errors:
|
||||||
|
if hasattr(self, 'parent_manager'):
|
||||||
|
if not self.parent_manager.handle_read_error():
|
||||||
|
self.error_signal.emit("Critical error - stopping measurement")
|
||||||
|
break
|
||||||
|
consecutive_errors = 0
|
||||||
|
|
||||||
def set_direction(self, direction):
|
def set_direction(self, direction):
|
||||||
"""Set current direction (1 for source, -1 for sink)"""
|
"""Set current direction (1 for source, -1 for sink)"""
|
||||||
@ -1039,7 +1048,7 @@ class BatteryTester(QMainWindow):
|
|||||||
self.main_layout.addWidget(self.canvas, 1)
|
self.main_layout.addWidget(self.canvas, 1)
|
||||||
|
|
||||||
def init_device(self):
|
def init_device(self):
|
||||||
"""Initialize all ADALM1000 with proper permission handling"""
|
"""Initialize ADALM1000 devices with proper error handling"""
|
||||||
try:
|
try:
|
||||||
# Cleanup previous session
|
# Cleanup previous session
|
||||||
if hasattr(self, 'session'):
|
if hasattr(self, 'session'):
|
||||||
@ -1047,30 +1056,34 @@ class BatteryTester(QMainWindow):
|
|||||||
self.session.end()
|
self.session.end()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
del self.session
|
|
||||||
|
|
||||||
self.session = PatchedSession(ignore_dataflow=True, queue_size=10000)
|
self.session = pysmu.Session(ignore_dataflow=True, queue_size=10000)
|
||||||
|
|
||||||
|
# Retry mechanism with progress feedback
|
||||||
|
retry_count = 0
|
||||||
|
while not self.session.devices and retry_count < 3:
|
||||||
|
self.status_bar.showMessage(f"Scanning for devices... (Attempt {retry_count + 1}/3)")
|
||||||
|
QApplication.processEvents() # Update UI
|
||||||
|
time.sleep(1)
|
||||||
|
self.session.scan() # Manual scan
|
||||||
|
retry_count += 1
|
||||||
|
|
||||||
if not self.session.devices:
|
if not self.session.devices:
|
||||||
self.status_bar.showMessage("No ADALM1000 devices found")
|
self.handle_no_devices()
|
||||||
self.session_active = False
|
|
||||||
self.active_device = None # Sicherstellen, dass es None ist
|
|
||||||
return
|
return
|
||||||
|
|
||||||
self.session.start(0)
|
self.session.start(0)
|
||||||
self.devices.clear()
|
self.devices = {}
|
||||||
for dev in self.session.devices:
|
for dev in self.session.devices:
|
||||||
manager = DeviceManager(dev)
|
manager = DeviceManager(dev)
|
||||||
manager.start_measurement(interval=self.interval)
|
manager.start_measurement(interval=self.interval)
|
||||||
self.devices[dev.serial] = manager
|
self.devices[dev.serial] = manager
|
||||||
|
|
||||||
self.session.start(0)
|
# Select first device
|
||||||
|
first_serial = next(iter(self.devices.keys()))
|
||||||
# Erstes Gerät aktivieren
|
|
||||||
first_serial = self.session.devices[0].serial
|
|
||||||
self.active_device = self.devices[first_serial]
|
self.active_device = self.devices[first_serial]
|
||||||
|
|
||||||
# UI aktualisieren
|
# Update UI
|
||||||
self.device_combo.clear()
|
self.device_combo.clear()
|
||||||
for serial in self.devices:
|
for serial in self.devices:
|
||||||
self.device_combo.addItem(serial)
|
self.device_combo.addItem(serial)
|
||||||
@ -1081,13 +1094,26 @@ class BatteryTester(QMainWindow):
|
|||||||
self.status_light.setStyleSheet("background-color: green; border-radius: 10px;")
|
self.status_light.setStyleSheet("background-color: green; border-radius: 10px;")
|
||||||
self.start_button.setEnabled(True)
|
self.start_button.setEnabled(True)
|
||||||
|
|
||||||
# Starte Mess-Update für aktives Gerät
|
# Connect measurement signals
|
||||||
self.measurement_thread = self.active_device.measurement_thread
|
self.measurement_thread = self.active_device.measurement_thread
|
||||||
self.measurement_thread.update_signal.connect(self.update_measurements)
|
self.measurement_thread.update_signal.connect(self.update_measurements)
|
||||||
self.measurement_thread.error_signal.connect(self.handle_device_error)
|
self.measurement_thread.error_signal.connect(self.handle_device_error)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.handle_device_error(str(e))
|
self.handle_device_error(f"Initialization failed: {str(e)}")
|
||||||
|
|
||||||
|
def handle_no_devices(self):
|
||||||
|
"""Handle case when no devices are found"""
|
||||||
|
self.session_active = False
|
||||||
|
self.active_device = None
|
||||||
|
self.status_bar.showMessage("No ADALM1000 devices found")
|
||||||
|
self.status_light.setStyleSheet("background-color: red; border-radius: 10px;")
|
||||||
|
self.start_button.setEnabled(False)
|
||||||
|
self.device_combo.clear()
|
||||||
|
|
||||||
|
# Show reconnect button
|
||||||
|
self.reconnect_btn.setEnabled(True)
|
||||||
|
self.reconnect_btn.setVisible(True)
|
||||||
|
|
||||||
def request_usb_permissions(self):
|
def request_usb_permissions(self):
|
||||||
"""Handle USB permission issues with user interaction"""
|
"""Handle USB permission issues with user interaction"""
|
||||||
@ -1219,7 +1245,9 @@ class BatteryTester(QMainWindow):
|
|||||||
|
|
||||||
@pyqtSlot(float, float, float)
|
@pyqtSlot(float, float, float)
|
||||||
def update_measurements(self, voltage, current, current_time):
|
def update_measurements(self, voltage, current, current_time):
|
||||||
if not self.active_device:
|
# Add measurement validation
|
||||||
|
if not self.validate_measurements(voltage, current):
|
||||||
|
print(f"Invalid measurement: V={voltage:.4f}, I={current:.4f}")
|
||||||
return
|
return
|
||||||
|
|
||||||
dev_manager = self.active_device
|
dev_manager = self.active_device
|
||||||
@ -2301,16 +2329,20 @@ class BatteryTester(QMainWindow):
|
|||||||
self.update_plot()
|
self.update_plot()
|
||||||
|
|
||||||
def update_plot(self):
|
def update_plot(self):
|
||||||
"""More robust plotting with error handling"""
|
"""Fixed plot method with safe attribute access"""
|
||||||
try:
|
try:
|
||||||
|
if not self.active_device:
|
||||||
|
return
|
||||||
|
|
||||||
# Create local copies of data safely
|
# Create local copies of data safely
|
||||||
with self.plot_mutex:
|
with self.plot_mutex:
|
||||||
if not self.display_time_data:
|
dev = self.active_device
|
||||||
|
if not dev.display_time_data:
|
||||||
return
|
return
|
||||||
|
|
||||||
x_data = np.array(self.display_time_data)
|
x_data = list(dev.display_time_data)
|
||||||
y1_data = np.array(self.display_voltage_data)
|
y1_data = list(dev.display_voltage_data)
|
||||||
y2_data = np.array(self.display_current_data)
|
y2_data = list(dev.display_current_data)
|
||||||
|
|
||||||
# Update plot data
|
# Update plot data
|
||||||
self.line_voltage.set_data(x_data, y1_data)
|
self.line_voltage.set_data(x_data, y1_data)
|
||||||
@ -2326,7 +2358,10 @@ class BatteryTester(QMainWindow):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Plot error: {e}")
|
print(f"Plot error: {e}")
|
||||||
# Attempt to recover
|
# Attempt to recover
|
||||||
self.reset_plot()
|
try:
|
||||||
|
self.reset_plot()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def auto_scale_axes(self):
|
def auto_scale_axes(self):
|
||||||
"""Auto-scale plot axes with appropriate padding and strict boundaries"""
|
"""Auto-scale plot axes with appropriate padding and strict boundaries"""
|
||||||
@ -2360,24 +2395,30 @@ class BatteryTester(QMainWindow):
|
|||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def handle_device_error(self, error_msg):
|
def handle_device_error(self, error_msg):
|
||||||
"""Handle device errors gracefully"""
|
"""Enhanced error handling with recovery"""
|
||||||
print(f"EXAKTER FEHLER: {error_msg}")
|
print(f"DEVICE ERROR: {error_msg}")
|
||||||
self.status_bar.showMessage(f"Device error: {error_msg}")
|
|
||||||
self.status_light.setStyleSheet("background-color: red; border-radius: 10px;")
|
|
||||||
self.session_active = False
|
|
||||||
self.test_running = False
|
|
||||||
self.measuring = False
|
|
||||||
self.start_button.setEnabled(False)
|
|
||||||
self.stop_button.setEnabled(False)
|
|
||||||
|
|
||||||
# Clear device-specific data ONLY if we have an active device
|
# Special handling for USB errors
|
||||||
if self.active_device:
|
if "USB" in error_msg or "LIBUSB" in error_msg.upper():
|
||||||
data = self.active_device
|
error_msg += "\n\nCheck USB connection and permissions"
|
||||||
with self.plot_mutex:
|
self.status_light.setStyleSheet("background-color: red;")
|
||||||
data.time_data.clear()
|
else:
|
||||||
data.voltage_data.clear()
|
self.status_light.setStyleSheet("background-color: orange;")
|
||||||
data.current_data.clear()
|
|
||||||
self.update_plot()
|
self.status_bar.showMessage(f"Device error: {error_msg}")
|
||||||
|
|
||||||
|
# Attempt automatic recovery for non-critical errors
|
||||||
|
if "No samples" in error_msg or "timed out" in error_msg:
|
||||||
|
QTimer.singleShot(1000, self.reconnect_device)
|
||||||
|
|
||||||
|
def validate_measurements(self, voltage, current):
|
||||||
|
"""Filter out invalid measurements"""
|
||||||
|
# Fix negative values caused by connection issues
|
||||||
|
if voltage < 0 or not (0 <= voltage <= 5.0):
|
||||||
|
return False
|
||||||
|
if abs(current) > 0.3: # Beyond ADALM1000's ±200mA range
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def update_test_phase(self, phase_text):
|
def update_test_phase(self, phase_text):
|
||||||
@ -2434,52 +2475,129 @@ class BatteryTester(QMainWindow):
|
|||||||
QTimer.singleShot(1000, self.reconnect_device)
|
QTimer.singleShot(1000, self.reconnect_device)
|
||||||
|
|
||||||
def reconnect_device(self):
|
def reconnect_device(self):
|
||||||
if hasattr(self, 'measurement_thread'):
|
"""Robuste Geräte-Neuerkennung mit vollständigem Reset und Statusfeedback"""
|
||||||
self.measurement_thread.stop()
|
self.status_bar.showMessage("Starting device reconnection...")
|
||||||
self.measurement_thread.wait(500)
|
self.status_light.setStyleSheet("background-color: orange; border-radius: 10px;")
|
||||||
|
QApplication.processEvents() # Sofortiges UI-Update erzwingen
|
||||||
|
|
||||||
|
# 1. Vorhandene Verbindungen sauber beenden
|
||||||
try:
|
try:
|
||||||
self.session = PatchedSession(ignore_dataflow=True, queue_size=10000)
|
if hasattr(self, 'measurement_thread'):
|
||||||
if not self.session.devices:
|
self.measurement_thread.stop()
|
||||||
raise DeviceDisconnectedError("No devices found")
|
if not self.measurement_thread.wait(1000): # Timeout 1s
|
||||||
|
self.measurement_thread.terminate()
|
||||||
new_serials = {dev.serial for dev in self.session.devices}
|
|
||||||
old_serials = set(self.devices.keys())
|
|
||||||
|
|
||||||
# Neue hinzufügen
|
|
||||||
for dev in self.session.devices:
|
|
||||||
if dev.serial not in self.devices:
|
|
||||||
manager = DeviceManager(dev)
|
|
||||||
manager.start_measurement(self.interval)
|
|
||||||
self.devices[dev.serial] = manager
|
|
||||||
|
|
||||||
# Entfernte entfernen
|
|
||||||
for serial in list(self.devices.keys()):
|
|
||||||
if serial not in new_serials:
|
|
||||||
self.devices[serial].stop_measurement()
|
|
||||||
del self.devices[serial]
|
|
||||||
|
|
||||||
# Aktualisiere UI
|
|
||||||
current_serial = self.active_device.serial if self.active_device else None
|
|
||||||
self.device_combo.clear()
|
|
||||||
for serial in self.devices:
|
|
||||||
self.device_combo.addItem(serial)
|
|
||||||
|
|
||||||
if current_serial in self.devices:
|
|
||||||
self.device_combo.setCurrentText(current_serial)
|
|
||||||
self.active_device = self.devices[current_serial]
|
|
||||||
else:
|
|
||||||
first = next(iter(self.devices))
|
|
||||||
self.device_combo.setCurrentText(first)
|
|
||||||
self.active_device = self.devices[first]
|
|
||||||
|
|
||||||
self.measurement_thread = self.active_device.measurement_thread
|
|
||||||
self.measurement_thread.update_signal.connect(self.update_measurements)
|
|
||||||
|
|
||||||
self.connection_label.setText(f"Reconnected: {self.active_device.serial}")
|
|
||||||
self.status_bar.showMessage("Reconnected all devices")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
QTimer.singleShot(2000, self.reconnect_device)
|
print(f"Error stopping measurement thread: {e}")
|
||||||
|
|
||||||
|
# 2. Alte Session bereinigen
|
||||||
|
if hasattr(self, 'session'):
|
||||||
|
try:
|
||||||
|
self.session.end()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error ending session: {e}")
|
||||||
|
finally:
|
||||||
|
del self.session
|
||||||
|
|
||||||
|
# 3. Neue Session mit Fortschrittsfeedback
|
||||||
|
retry_count = 0
|
||||||
|
max_retries = 3
|
||||||
|
reconnect_delay = 2000 # ms
|
||||||
|
|
||||||
|
while retry_count < max_retries:
|
||||||
|
try:
|
||||||
|
self.status_bar.showMessage(f"Scanning for devices (Attempt {retry_count + 1}/{max_retries})...")
|
||||||
|
QApplication.processEvents()
|
||||||
|
|
||||||
|
# Neue Session erstellen
|
||||||
|
self.session = pysmu.Session(ignore_dataflow=True, queue_size=10000)
|
||||||
|
|
||||||
|
# Manueller Scan mit Timeout
|
||||||
|
scan_success = False
|
||||||
|
for _ in range(2): # Max 2 Scan-Versuche
|
||||||
|
self.session.scan()
|
||||||
|
if self.session.devices:
|
||||||
|
scan_success = True
|
||||||
|
break
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
if not scan_success:
|
||||||
|
raise DeviceDisconnectedError("No devices detected")
|
||||||
|
|
||||||
|
# 4. Geräteliste aktualisieren
|
||||||
|
current_devices = {dev.serial: dev for dev in self.session.devices}
|
||||||
|
old_devices = self.devices.copy() if hasattr(self, 'devices') else {}
|
||||||
|
|
||||||
|
# Neue Geräte hinzufügen
|
||||||
|
new_devices = {}
|
||||||
|
for serial, dev in current_devices.items():
|
||||||
|
if serial in old_devices:
|
||||||
|
# Bestehendes Gerät wiederverwenden
|
||||||
|
new_devices[serial] = old_devices[serial]
|
||||||
|
else:
|
||||||
|
# Neues Gerät initialisieren
|
||||||
|
self.status_bar.showMessage(f"Initializing device {serial}...")
|
||||||
|
QApplication.processEvents()
|
||||||
|
manager = DeviceManager(dev)
|
||||||
|
manager.start_measurement(self.interval)
|
||||||
|
new_devices[serial] = manager
|
||||||
|
|
||||||
|
# Nicht mehr vorhandene Geräte entfernen
|
||||||
|
for serial, manager in old_devices.items():
|
||||||
|
if serial not in current_devices:
|
||||||
|
try:
|
||||||
|
manager.stop_measurement()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error stopping device {serial}: {e}")
|
||||||
|
|
||||||
|
self.devices = new_devices
|
||||||
|
|
||||||
|
# 5. Aktives Gerät auswählen
|
||||||
|
current_serial = self.active_device.serial if (hasattr(self, 'active_device') and self.active_device) else None
|
||||||
|
|
||||||
|
# UI aktualisieren
|
||||||
|
self.device_combo.clear()
|
||||||
|
for serial in self.devices:
|
||||||
|
self.device_combo.addItem(serial)
|
||||||
|
|
||||||
|
if current_serial in self.devices:
|
||||||
|
self.device_combo.setCurrentText(current_serial)
|
||||||
|
self.active_device = self.devices[current_serial]
|
||||||
|
elif self.devices:
|
||||||
|
first_serial = next(iter(self.devices))
|
||||||
|
self.device_combo.setCurrentText(first_serial)
|
||||||
|
self.active_device = self.devices[first_serial]
|
||||||
|
else:
|
||||||
|
raise DeviceDisconnectedError("No valid devices available")
|
||||||
|
|
||||||
|
# Signalverbindungen herstellen
|
||||||
|
if hasattr(self.active_device, 'measurement_thread'):
|
||||||
|
self.measurement_thread = self.active_device.measurement_thread
|
||||||
|
self.measurement_thread.update_signal.connect(self.update_measurements)
|
||||||
|
self.measurement_thread.error_signal.connect(self.handle_device_error)
|
||||||
|
|
||||||
|
# Erfolgsmeldung
|
||||||
|
self.connection_label.setText(f"Connected: {self.active_device.serial}")
|
||||||
|
self.status_bar.showMessage("Successfully reconnected devices")
|
||||||
|
self.status_light.setStyleSheet("background-color: green; border-radius: 10px;")
|
||||||
|
self.start_button.setEnabled(True)
|
||||||
|
return
|
||||||
|
|
||||||
|
except DeviceDisconnectedError as e:
|
||||||
|
retry_count += 1
|
||||||
|
if retry_count < max_retries:
|
||||||
|
self.status_bar.showMessage(f"Reconnect failed: {e}. Retrying in {reconnect_delay/1000}s...")
|
||||||
|
QApplication.processEvents()
|
||||||
|
time.sleep(reconnect_delay/1000)
|
||||||
|
else:
|
||||||
|
self.status_bar.showMessage(f"Reconnect failed after {max_retries} attempts")
|
||||||
|
self.status_light.setStyleSheet("background-color: red; border-radius: 10px;")
|
||||||
|
QTimer.singleShot(reconnect_delay, self.attempt_reconnect)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Critical reconnect error: {traceback.format_exc()}")
|
||||||
|
self.status_bar.showMessage(f"Critical error: {str(e)}")
|
||||||
|
self.status_light.setStyleSheet("background-color: red; border-radius: 10px;")
|
||||||
|
QTimer.singleShot(reconnect_delay, self.attempt_reconnect)
|
||||||
|
return
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
"""Clean up on window close"""
|
"""Clean up on window close"""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user