MainCode/adalm1000_logger.py aktualisiert
Key-Improvements:
Korrekte Session-Handling:
session.start() immer mit explizitem Device-Index
Konsistente Initialisierung bei allen Funktionen
Robustere Geräteauswahl:
Serialnummern-basierte Wiedererkennung beim Reconnect
Sichere Index-Prüfungen
Bessere Fehlerbehandlung:
Explizite Cleanup-Schritte
Zustandserhaltung bei Neustarts
Klarere Statusmeldungen:
Seriennummern in UI-Elementen
Detaillierte Fehlermeldungen
D+C
This commit is contained in:
parent
a26f6b8fe5
commit
e443408824
@ -33,13 +33,16 @@ class MeasurementThread(QThread):
|
||||
self.filter_window_size = 10
|
||||
self.voltage_window = []
|
||||
self.current_window = []
|
||||
self.start_time = time.time()
|
||||
self.start_time = None
|
||||
self.measurement_queue = Queue(maxsize=1)
|
||||
self.current_direction = 1 # 1 for source, -1 for sink
|
||||
|
||||
def run(self):
|
||||
"""Continuous measurement loop"""
|
||||
self._running = True
|
||||
if self.start_time is None: # Nur setzen wenn noch nicht gesetzt
|
||||
self.start_time = time.time()
|
||||
|
||||
while self._running:
|
||||
try:
|
||||
samples = self.device.read(self.filter_window_size, 500, True)
|
||||
@ -816,7 +819,8 @@ class BatteryTester(QMainWindow):
|
||||
# Reset Downsampling
|
||||
self.downsample_factor = 1
|
||||
self.downsample_counter = 0
|
||||
# Clear data buffers
|
||||
|
||||
# Clear all data buffers
|
||||
with self.plot_mutex:
|
||||
self.time_data.clear()
|
||||
self.voltage_data.clear()
|
||||
@ -824,6 +828,26 @@ class BatteryTester(QMainWindow):
|
||||
if hasattr(self, 'phase_data'):
|
||||
self.phase_data.clear()
|
||||
|
||||
# Also clear display buffers
|
||||
if hasattr(self, 'display_time_data'):
|
||||
self.display_time_data.clear()
|
||||
self.display_voltage_data.clear()
|
||||
self.display_current_data.clear()
|
||||
|
||||
# Reset aggregation buffer
|
||||
self.aggregation_buffer = {
|
||||
'time': [], 'voltage': [], 'current': [],
|
||||
'count': 0, 'last_plot_time': 0
|
||||
}
|
||||
|
||||
# Clear measurement thread buffers if it exists
|
||||
if hasattr(self, 'measurement_thread'):
|
||||
self.measurement_thread.voltage_window.clear()
|
||||
self.measurement_thread.current_window.clear()
|
||||
with self.measurement_thread.measurement_queue.mutex:
|
||||
self.measurement_thread.measurement_queue.queue.clear()
|
||||
self.measurement_thread.start_time = time.time()
|
||||
|
||||
# Reset capacities and timing
|
||||
self.start_time = time.time()
|
||||
self.last_update_time = self.start_time
|
||||
@ -847,6 +871,13 @@ class BatteryTester(QMainWindow):
|
||||
if self.record_button.isChecked():
|
||||
# Start recording
|
||||
try:
|
||||
# Reset previous data
|
||||
self.reset_test()
|
||||
|
||||
# Reset measurement timing
|
||||
if hasattr(self, 'measurement_thread'):
|
||||
self.measurement_thread.start_time = time.time()
|
||||
|
||||
if self.create_cycle_log_file():
|
||||
self.record_button.setText("Stop Recording")
|
||||
self.status_bar.showMessage("Live recording started")
|
||||
@ -855,11 +886,11 @@ class BatteryTester(QMainWindow):
|
||||
self.start_live_monitoring()
|
||||
else:
|
||||
self.record_button.setChecked(False)
|
||||
self.current_cycle_file = None # Ensure it's None if creation failed
|
||||
self.current_cycle_file = None
|
||||
except Exception as e:
|
||||
print(f"Error starting recording: {e}")
|
||||
self.record_button.setChecked(False)
|
||||
self.current_cycle_file = None # Ensure it's None on error
|
||||
self.current_cycle_file = None
|
||||
QMessageBox.critical(self, "Error", f"Failed to start recording:\n{str(e)}")
|
||||
else:
|
||||
# Stop recording
|
||||
@ -922,37 +953,54 @@ class BatteryTester(QMainWindow):
|
||||
self.main_layout.addWidget(self.canvas, 1)
|
||||
|
||||
def init_device(self):
|
||||
"""Initialize the ADALM1000 device with continuous measurement"""
|
||||
"""Initialize ADALM1000 with proper device selection and session handling"""
|
||||
try:
|
||||
# Clean up any existing session
|
||||
# Clean up existing session
|
||||
if hasattr(self, 'session'):
|
||||
try:
|
||||
self.session.end()
|
||||
del self.session
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Error cleaning up session: {e}")
|
||||
|
||||
time.sleep(1)
|
||||
time.sleep(0.5) # Brief pause for USB re-enumeration
|
||||
|
||||
# Initialize new session
|
||||
self.session = pysmu.Session(ignore_dataflow=True, queue_size=10000)
|
||||
if not self.session.devices:
|
||||
raise Exception("No ADALM1000 detected - check connections")
|
||||
raise DeviceDisconnectedError("No ADALM1000 devices detected")
|
||||
|
||||
# Populate device selector
|
||||
self.device_combo.clear()
|
||||
for dev in self.session.devices:
|
||||
self.device_combo.addItem(dev.serial)
|
||||
|
||||
# Select first device by default
|
||||
self.dev = self.session.devices[0]
|
||||
self.device_combo.setCurrentIndex(0)
|
||||
|
||||
# Configure channels
|
||||
self.dev.channels['A'].mode = pysmu.Mode.HI_Z
|
||||
self.dev.channels['B'].mode = pysmu.Mode.HI_Z
|
||||
self.dev.channels['A'].constant(0)
|
||||
self.dev.channels['B'].constant(0)
|
||||
|
||||
self.session.start(0)
|
||||
# Start session for the selected device
|
||||
device_index = self.session.devices.index(self.dev)
|
||||
self.session.start(device_index)
|
||||
|
||||
self.status_light.setStyleSheet(f"background-color: green; border-radius: 10px;")
|
||||
self.connection_label.setText("Connected")
|
||||
self.status_bar.showMessage("Device connected | Ready to measure")
|
||||
# Update UI
|
||||
self.status_light.setStyleSheet("background-color: green; border-radius: 10px;")
|
||||
self.connection_label.setText(f"Connected: {self.dev.serial}")
|
||||
self.status_bar.showMessage(f"Ready - Device {self.dev.serial}")
|
||||
self.session_active = True
|
||||
self.start_button.setEnabled(True)
|
||||
|
||||
# Start measurement thread
|
||||
if hasattr(self, 'measurement_thread'):
|
||||
self.measurement_thread.stop()
|
||||
self.measurement_thread.wait(500)
|
||||
|
||||
self.measurement_thread = MeasurementThread(self.dev, self.interval)
|
||||
self.measurement_thread.update_signal.connect(self.update_measurements)
|
||||
self.measurement_thread.error_signal.connect(self.handle_device_error)
|
||||
@ -961,6 +1009,47 @@ class BatteryTester(QMainWindow):
|
||||
except Exception as e:
|
||||
self.handle_device_error(str(e))
|
||||
|
||||
def change_device(self, index):
|
||||
"""Safely switch to another ADALM1000 device"""
|
||||
if not self.session_active or index < 0 or index >= len(self.session.devices):
|
||||
return
|
||||
|
||||
try:
|
||||
# Stop current operations
|
||||
self.stop_test()
|
||||
|
||||
# Stop measurement thread
|
||||
if hasattr(self, 'measurement_thread'):
|
||||
self.measurement_thread.stop()
|
||||
self.measurement_thread.wait(500)
|
||||
|
||||
# Switch to new device
|
||||
self.dev = self.session.devices[index]
|
||||
|
||||
# Reconfigure channels
|
||||
self.dev.channels['A'].mode = pysmu.Mode.HI_Z
|
||||
self.dev.channels['B'].mode = pysmu.Mode.HI_Z
|
||||
self.dev.channels['A'].constant(0)
|
||||
self.dev.channels['B'].constant(0)
|
||||
|
||||
# Restart session for new device
|
||||
device_index = self.session.devices.index(self.dev)
|
||||
self.session.start(device_index)
|
||||
|
||||
# Update UI
|
||||
self.device_combo.setCurrentIndex(index)
|
||||
self.connection_label.setText(f"Connected: {self.dev.serial}")
|
||||
self.status_bar.showMessage(f"Switched to device {self.dev.serial}")
|
||||
|
||||
# Restart measurement
|
||||
self.measurement_thread = MeasurementThread(self.dev, self.interval)
|
||||
self.measurement_thread.update_signal.connect(self.update_measurements)
|
||||
self.measurement_thread.error_signal.connect(self.handle_device_error)
|
||||
self.measurement_thread.start()
|
||||
|
||||
except Exception as e:
|
||||
self.handle_device_error(f"Device switch failed: {str(e)}")
|
||||
|
||||
@pyqtSlot(float, float, float)
|
||||
def update_measurements(self, voltage, current, current_time):
|
||||
try:
|
||||
@ -1085,19 +1174,29 @@ class BatteryTester(QMainWindow):
|
||||
|
||||
def update_status(self):
|
||||
"""Update status information periodically"""
|
||||
now = time.time() # Define 'now' at the start of the method
|
||||
|
||||
if self.test_running or hasattr(self, 'record_button') and self.record_button.isChecked():
|
||||
if self.time_data:
|
||||
current_time = self.time_data[-1] # Verwenden Sie den gespeicherten relativen Zeitstempel
|
||||
current_time = self.time_data[-1]
|
||||
if len(self.time_data) > 1:
|
||||
delta_t = self.time_data[-1] - self.time_data[-2] # Korrekte Zeitdifferenz
|
||||
if delta_t > 0: # Nur positive Differenzen berücksichtigen
|
||||
delta_t = self.time_data[-1] - self.time_data[-2]
|
||||
if delta_t > 0:
|
||||
current_current = abs(self.current_data[-1])
|
||||
self.capacity_ah += current_current * delta_t / 3600
|
||||
self.capacity_label.setText(f"{self.capacity_ah:.4f}")
|
||||
|
||||
# Logging (1x per second)
|
||||
if hasattr(self, 'log_writer') and hasattr(self, 'current_cycle_file') and self.current_cycle_file is not None:
|
||||
if self.time_data and not self.current_cycle_file.closed and (now - self._last_log_time >= 1.0):
|
||||
if (hasattr(self, 'log_writer') and
|
||||
hasattr(self, 'current_cycle_file') and
|
||||
self.current_cycle_file is not None and
|
||||
not self.current_cycle_file.closed):
|
||||
|
||||
# Initialize last log time if not exists
|
||||
if not hasattr(self, '_last_log_time'):
|
||||
self._last_log_time = now
|
||||
|
||||
if self.time_data and (now - self._last_log_time >= 1.0):
|
||||
try:
|
||||
current_time = self.time_data[-1]
|
||||
voltage = self.voltage_data[-1]
|
||||
@ -1449,19 +1548,12 @@ class BatteryTester(QMainWindow):
|
||||
def start_live_monitoring(self):
|
||||
"""Start live monitoring mode"""
|
||||
try:
|
||||
# Clear previous data
|
||||
with self.plot_mutex:
|
||||
self.time_data.clear()
|
||||
self.voltage_data.clear()
|
||||
self.current_data.clear()
|
||||
# Reset everything completely
|
||||
self.reset_test()
|
||||
|
||||
# Reset timing and measurements
|
||||
# Reset measurement timing
|
||||
if hasattr(self, 'measurement_thread'):
|
||||
self.measurement_thread.start_time = time.time()
|
||||
self.start_time = time.time()
|
||||
self.last_update_time = self.start_time
|
||||
self.capacity_ah = 0.0
|
||||
self.energy = 0.0
|
||||
|
||||
# Set monitoring flags
|
||||
self.test_running = True
|
||||
@ -1490,6 +1582,7 @@ class BatteryTester(QMainWindow):
|
||||
def create_cycle_log_file(self):
|
||||
"""Create a new log file for the current test"""
|
||||
try:
|
||||
self._last_log_time = time.time()
|
||||
# Close previous file if exists
|
||||
if hasattr(self, 'current_cycle_file') and self.current_cycle_file:
|
||||
try:
|
||||
@ -1858,35 +1951,36 @@ class BatteryTester(QMainWindow):
|
||||
|
||||
def reset_plot(self):
|
||||
"""Completely reset the plot - clears all data and visuals"""
|
||||
# 1. Clear line data
|
||||
# Clear line data
|
||||
self.line_voltage.set_data([], [])
|
||||
self.line_current.set_data([], [])
|
||||
|
||||
# 2. Clear data buffers
|
||||
self.time_data.clear()
|
||||
self.voltage_data.clear()
|
||||
self.current_data.clear()
|
||||
if hasattr(self, 'phase_data'):
|
||||
self.phase_data.clear()
|
||||
|
||||
# 3. Reset axes with appropriate ranges
|
||||
# Reset axes with appropriate ranges
|
||||
voltage_padding = 0.2
|
||||
min_voltage = 0
|
||||
max_voltage = 5.0 # Max voltage for ADALM1000
|
||||
|
||||
self.ax.set_xlim(0, 10) # Reset X axis
|
||||
self.ax.set_ylim(min_voltage, max_voltage)
|
||||
self.ax.set_xlabel('Time (s)', color=self.fg_color)
|
||||
self.ax.set_ylabel("Voltage (V)", color='#00BFFF')
|
||||
self.ax.set_title('Battery Test', color=self.fg_color)
|
||||
self.ax.tick_params(axis='x', colors=self.fg_color)
|
||||
self.ax.tick_params(axis='y', labelcolor='#00BFFF')
|
||||
self.ax.grid(True, color='#4C566A')
|
||||
|
||||
# Reset twin axis (current)
|
||||
current_padding = 0.05
|
||||
self.ax2.set_xlim(0, 10)
|
||||
self.ax2.set_ylim(-0.25 - current_padding, 0.25 + current_padding)
|
||||
self.ax2.set_ylabel("Current (A)", color='r')
|
||||
self.ax2.tick_params(axis='y', labelcolor='r')
|
||||
|
||||
# 4. Clear any matplotlib internal caches
|
||||
self.fig.canvas.draw_idle()
|
||||
self.fig.canvas.flush_events()
|
||||
# Redraw legends
|
||||
self.ax.legend(loc='upper left', bbox_to_anchor=(0.01, 0.99))
|
||||
self.ax2.legend(loc='upper right', bbox_to_anchor=(0.99, 0.99))
|
||||
|
||||
# 5. Force immediate redraw
|
||||
# Force immediate redraw
|
||||
self.canvas.draw()
|
||||
|
||||
def update_status_and_plot(self):
|
||||
@ -2037,35 +2131,70 @@ class BatteryTester(QMainWindow):
|
||||
QTimer.singleShot(1000, self.reconnect_device)
|
||||
|
||||
def reconnect_device(self):
|
||||
"""Reconnect the device with proper cleanup"""
|
||||
self.status_bar.showMessage("Attempting to reconnect...")
|
||||
"""Robust reconnection handler with device persistence"""
|
||||
self.status_bar.showMessage("Reconnecting...")
|
||||
|
||||
# Remember current selection
|
||||
current_serial = self.dev.serial if hasattr(self, 'dev') else None
|
||||
|
||||
# Cleanup existing connection
|
||||
if hasattr(self, 'session'):
|
||||
try:
|
||||
if self.session_active:
|
||||
self.session.end()
|
||||
del self.session
|
||||
except:
|
||||
pass
|
||||
|
||||
self.test_running = False
|
||||
self.continuous_mode = False
|
||||
self.measuring = False
|
||||
except Exception as e:
|
||||
print(f"Error ending session: {e}")
|
||||
|
||||
if hasattr(self, 'measurement_thread'):
|
||||
self.measurement_thread.stop()
|
||||
self.measurement_thread.wait(500)
|
||||
|
||||
time.sleep(1.5)
|
||||
time.sleep(1) # Allow for USB reinitialization
|
||||
|
||||
try:
|
||||
self.init_device()
|
||||
if self.session_active:
|
||||
self.status_bar.showMessage("Reconnected successfully")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"Reconnect failed: {e}")
|
||||
# Reinitialize session
|
||||
self.session = pysmu.Session(ignore_dataflow=True, queue_size=10000)
|
||||
if not self.session.devices:
|
||||
raise DeviceDisconnectedError("No devices available")
|
||||
|
||||
self.status_bar.showMessage("Reconnect failed - will retry...")
|
||||
# Repopulate device list
|
||||
self.device_combo.clear()
|
||||
for dev in self.session.devices:
|
||||
self.device_combo.addItem(dev.serial)
|
||||
|
||||
# Try to reselect previous device
|
||||
target_index = 0
|
||||
if current_serial:
|
||||
for i, dev in enumerate(self.session.devices):
|
||||
if dev.serial == current_serial:
|
||||
target_index = i
|
||||
break
|
||||
|
||||
self.dev = self.session.devices[target_index]
|
||||
self.device_combo.setCurrentIndex(target_index)
|
||||
|
||||
# Configure and start
|
||||
self.dev.channels['A'].mode = pysmu.Mode.HI_Z
|
||||
self.dev.channels['B'].mode = pysmu.Mode.HI_Z
|
||||
self.dev.channels['A'].constant(0)
|
||||
self.dev.channels['B'].constant(0)
|
||||
|
||||
device_index = self.session.devices.index(self.dev)
|
||||
self.session.start(device_index)
|
||||
|
||||
# Update UI
|
||||
self.status_light.setStyleSheet("background-color: green; border-radius: 10px;")
|
||||
self.connection_label.setText(f"Reconnected: {self.dev.serial}")
|
||||
self.status_bar.showMessage(f"Device {self.dev.serial} ready")
|
||||
self.session_active = True
|
||||
|
||||
# Restart measurement
|
||||
self.measurement_thread = MeasurementThread(self.dev, self.interval)
|
||||
self.measurement_thread.update_signal.connect(self.update_measurements)
|
||||
self.measurement_thread.error_signal.connect(self.handle_device_error)
|
||||
self.measurement_thread.start()
|
||||
|
||||
except Exception as e:
|
||||
self.status_bar.showMessage("Reconnect failed - retrying...")
|
||||
QTimer.singleShot(2000, self.reconnect_device)
|
||||
|
||||
def closeEvent(self, event):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user