MainCode/adalm1000_logger.py aktualisiert

nearly working
D
This commit is contained in:
Jan 2025-08-08 01:39:48 +02:00
parent 041bcc09a6
commit cf6f91909f

View File

@ -76,7 +76,7 @@ class DeviceManager:
}
def handle_read_error(self, increment=1):
"""Enhanced device recovery with proper session handling"""
"""Enhanced device recovery with proper USB resource handling"""
self.consecutive_read_errors += increment
if self.consecutive_read_errors >= self.max_consecutive_errors:
@ -117,7 +117,23 @@ class DeviceManager:
# Add delay and attempt USB reset
time.sleep(2.0) # Longer delay for USB to settle
# Try to release USB resources using libusb
try:
import usb.core
# Find all ADALM1000 devices
devices = usb.core.find(find_all=True, idVendor=0x064b, idProduct=0x784c)
for dev in devices:
try:
print(f"Releasing USB device: {dev}")
usb.util.dispose_resources(dev)
except Exception as e:
print(f"Error releasing USB device: {e}")
time.sleep(1.0)
except ImportError:
print("pyusb not available, skipping resource release")
# Try multiple times to scan for devices
new_session = None
for attempt in range(3):
try:
print(f"Scan attempt {attempt + 1}/3...")
@ -130,27 +146,31 @@ class DeviceManager:
continue
# Find our device by serial
for new_dev in devices:
if new_dev.serial == self.serial:
print(f"Found device {self.serial} on attempt {attempt+1}")
self.dev = new_dev
self.consecutive_read_errors = 0
self.session = new_session # Update session reference
new_dev = None
for d in devices:
if d.serial == self.serial:
new_dev = d
break
# Restart measurement thread
if hasattr(self, 'measurement_thread'):
try:
self.measurement_thread.stop()
time.sleep(0.5)
except:
pass
self.start_measurement(self.interval)
if new_dev:
print(f"Found device {self.serial} on attempt {attempt+1}")
self.dev = new_dev
self.consecutive_read_errors = 0
self.session = new_session # Update session reference
print("Device reinitialized successfully")
return True
# Restart measurement thread
if hasattr(self, 'measurement_thread'):
try:
self.measurement_thread.stop()
time.sleep(0.5)
except:
pass
self.start_measurement(self.interval)
print(f"Original device not found on attempt {attempt+1}")
time.sleep(1.0)
print("Device reinitialized successfully")
return True
else:
print(f"Original device not found on attempt {attempt+1}")
except Exception as e:
print(f"Scan attempt {attempt+1} failed: {e}")
@ -161,28 +181,84 @@ class DeviceManager:
except Exception as e:
print(f"Full reinitialization failed: {e}")
return False
# Continue to final fallback even if this fails
pass
except Exception as e:
print(f"Device recovery failed: {e}")
# Continue to final fallback
pass
# 3. Final fallback - try USB reset command
try:
print("Attempting USB port reset...")
# ADALM1000 USB IDs
vendor_id = "064b"
product_id = "784c"
# 3. Final fallback - try USB reset command
try:
print("Attempting USB port reset...")
import subprocess
# ADALM1000 USB IDs
result = subprocess.run(['usbreset', '064b:784c'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
# Linux-specific reset
result = subprocess.run(['usbreset', f'{vendor_id}:{product_id}'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True)
if result.returncode == 0:
print("USB reset command executed, waiting 3 seconds...")
print("USB reset command executed successfully, waiting 3 seconds...")
time.sleep(3.0)
return self.handle_read_error(0) # Retry with counter reset
else:
print("USB reset failed - command not found or permission denied")
except Exception as e:
print(f"USB reset command failed: {e}")
print(f"USB reset command failed with code {result.returncode}")
print(f"Error output: {result.stderr}")
except FileNotFoundError:
# Windows alternative
try:
print("usbreset not found, trying Windows USB reset")
# Use devcon utility for Windows
result = subprocess.run(['devcon', 'restart', f'USB\VID_{vendor_id}&PID_{product_id}'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True)
if result.returncode == 0:
print("USB device reset via devcon, waiting 3 seconds...")
time.sleep(3.0)
return self.handle_read_error(0)
else:
print(f"devcon reset failed with code {result.returncode}")
print(f"Error output: {result.stderr}")
except FileNotFoundError:
# Python-based reset as final fallback
print("devcon not found, attempting Python USB reset")
try:
# Try to import USB module
try:
import usb.core
except ImportError:
print("pyusb not installed, please install with: pip install pyusb")
return False
# Find device by vendor and product ID
dev = usb.core.find(idVendor=0x064b, idProduct=0x784c)
if dev is not None:
try:
# Reset the USB device
dev.reset()
print("USB device reset via pyusb, waiting 3 seconds...")
time.sleep(3.0)
return self.handle_read_error(0)
except usb.core.USBError as e:
print(f"USB reset error: {e}")
else:
print("USB device not found for reset")
except Exception as e:
print(f"Python USB reset failed: {e}")
except Exception as e:
print(f"Device recovery failed: {e}")
return False
print(f"USB reset command failed: {e}")
import traceback
traceback.print_exc()
return True # Not enough errors yet to trigger recovery
@ -1127,37 +1203,29 @@ class BatteryTester(QMainWindow):
def toggle_global_recording(self):
"""Toggle recording for all connected devices simultaneously"""
if not hasattr(self, 'global_recording'):
self.global_recording = False
self.global_recording = not self.global_recording
if self.global_recording:
# Start recording for all devices
all_success = True
for serial, device in self.devices.items():
if not device.is_recording:
if not self.start_live_monitoring(device):
all_success = False
print(f"Failed to start recording for device {serial}")
for device in self.devices.values():
device.is_recording = True
if not device.measurement_thread.isRunning():
device.start_measurement(self.interval)
self.start_live_monitoring(device)
if all_success:
self.record_button.setText("■ Stop Recording")
self.apply_button_style() # Update style
self.status_bar.showMessage("Recording started for all connected devices")
else:
self.global_recording = False
self.record_button.setChecked(False)
QMessageBox.warning(self, "Warning", "Failed to start recording for some devices")
self.record_button.setText("■ Stop Recording")
self.status_bar.showMessage("Recording started for all connected devices")
else:
# Stop recording for all devices
for device in self.devices.values():
self.finalize_device_log_file(device) # Changed from _stop_device_recording
self.finalize_device_log_file(device)
device.is_recording = False
self.record_button.setText("● Start Recording")
self.apply_button_style() # Update style
self.status_bar.showMessage("Recording stopped for all devices")
self.apply_button_style()
def safe_execute(func):
"""Decorator to catch and log exceptions in Qt event handlers"""
def wrapper(*args, **kwargs):
@ -1380,13 +1448,18 @@ class BatteryTester(QMainWindow):
self.handle_device_connection(False, "No devices found")
return
self.session.start(0)
self.devices = {}
self.active_device = None
for dev in self.session.devices:
manager = DeviceManager(dev) # Should work now
manager = DeviceManager(dev)
manager.start_measurement(interval=self.interval)
self.devices[dev.serial] = manager
# Connect signals for all devices
manager.measurement_thread.update_signal.connect(self.update_measurements)
manager.measurement_thread.error_signal.connect(self.handle_device_error)
# Select first device
first_serial = next(iter(self.devices.keys()))
self.active_device = self.devices[first_serial]
@ -1536,60 +1609,36 @@ class BatteryTester(QMainWindow):
if serial not in self.devices:
return
# Stop current device
if self.active_device:
try:
# Disconnect signals safely
if hasattr(self.active_device, 'measurement_thread'):
thread = self.active_device.measurement_thread
try:
thread.stop() # Properly stop the thread
thread.wait(500) # Wait for clean shutdown
thread.update_signal.disconnect()
thread.error_signal.disconnect()
except (RuntimeError, TypeError) as e:
print(f"Signal disconnect warning: {e}")
except Exception as e:
print(f"Error stopping thread: {e}")
except Exception as e:
print(f"Error stopping previous device: {e}")
# Store current device state before switching
old_device = self.active_device
# Activate new device
self.active_device = self.devices[serial]
# Ensure measurement thread exists and is properly configured
if not hasattr(self.active_device, 'measurement_thread') or not self.active_device.measurement_thread:
self.active_device.start_measurement(self.interval)
# Connect signals to new device
# Reconnect signals for new device
self.measurement_thread = self.active_device.measurement_thread
# Disconnect any existing connections first
try:
self.measurement_thread.update_signal.disconnect()
except (TypeError, RuntimeError):
pass
try:
self.measurement_thread.error_signal.disconnect()
except (TypeError, RuntimeError):
pass
# Reconnect signals
self.measurement_thread.update_signal.connect(self.update_measurements)
self.measurement_thread.error_signal.connect(self.handle_device_error)
# Ensure thread is running
# Restart measurement if not running
if not self.measurement_thread.isRunning():
self.active_device.start_measurement(self.interval)
# Update UI with current device data
self.update_ui_from_active_device()
self.status_bar.showMessage(f"Switched to device: {serial}")
# Update recording button state
self.record_button.setChecked(self.global_recording)
self.record_button.setText("Stop Recording" if self.global_recording else "Start Recording")
self.apply_button_style() # Ensure proper colors
# Preserve recording state for old device
if old_device and old_device.is_recording:
# Ensure old device continues recording
if not old_device.measurement_thread.isRunning():
old_device.start_measurement(self.interval)
# Update recording button for new device
self.record_button.setChecked(self.active_device.is_recording)
self.record_button.setText("Stop Recording" if self.active_device.is_recording else "Start Recording")
self.apply_button_style()
self.status_bar.showMessage(f"Switched to device: {serial}")
def update_ui_from_active_device(self):
dev = self.active_device
@ -1622,52 +1671,77 @@ class BatteryTester(QMainWindow):
self.phase_label.setText(dev.test_phase)
@safe_execute
@pyqtSlot(float, float, float)
def update_measurements(self, voltage, current, current_time):
if not self.active_device:
@pyqtSlot(str, float, float, float)
def update_measurements(self, serial, voltage, current, current_time):
"""Update measurements for a specific device identified by serial"""
# Get device from serial
device = self.devices.get(serial)
if not device:
return
dev = self.active_device
# Update device data
dev.time_data.append(current_time)
dev.voltage_data.append(voltage)
dev.current_data.append(current)
device.time_data.append(current_time)
device.voltage_data.append(voltage)
device.current_data.append(current)
# Calculate metrics
power = voltage * abs(current) # Always define power
if len(dev.time_data) > 1:
delta_t = dev.time_data[-1] - dev.time_data[-2]
dev.capacity_ah += abs(current) * delta_t / 3600 # Ah
dev.energy += power * delta_t / 3600 # Wh
power = voltage * abs(current)
if len(device.time_data) > 1:
delta_t = device.time_data[-1] - device.time_data[-2]
device.capacity_ah += abs(current) * delta_t / 3600 # Ah
device.energy += power * delta_t / 3600 # Wh
# Update display buffers
dev.display_time_data.append(current_time)
dev.display_voltage_data.append(voltage)
dev.display_current_data.append(current)
device.display_time_data.append(current_time)
device.display_voltage_data.append(voltage)
device.display_current_data.append(current)
# Update UI for active device
self.voltage_label.setText(f"{voltage:.4f}")
self.current_label.setText(f"{abs(current):.4f}")
self.capacity_label.setText(f"{dev.capacity_ah:.4f}")
self.energy_label.setText(f"{dev.energy:.4f}")
# Update UI only if this is the active device
if device == self.active_device:
self.voltage_label.setText(f"{voltage:.4f}")
self.current_label.setText(f"{abs(current):.4f}")
self.capacity_label.setText(f"{device.capacity_ah:.4f}")
self.energy_label.setText(f"{device.energy:.4f}")
self.time_label.setText(self.format_time(current_time))
# Handle recording for ALL devices
# Update plot if needed (only for active device)
if time.time() - getattr(device, '_last_plot_update', 0) > 0.5: # Throttle updates
device._last_plot_update = time.time()
self.update_plot()
# Handle recording for this device
now = time.time()
for device in self.devices.values():
if device.is_recording and device.log_writer and device.time_data:
if now - device._last_log_time >= 1.0: # Log at 1Hz
if device.is_recording and device.log_writer and device.time_data:
if now - device._last_log_time >= 1.0: # Log at 1Hz
try:
device.log_writer.writerow([
f"{current_time:.2f}",
f"{voltage:.6f}",
f"{current:.6f}",
f"{dev.capacity_ah:.6f}",
f"{device.capacity_ah:.6f}",
f"{power:.6f}",
f"{dev.energy:.6f}",
dev.test_phase
f"{device.energy:.6f}",
device.test_phase
])
device.log_file.flush()
device._last_log_time = now
except Exception as e:
print(f"Log write error for device {serial}: {e}")
# Attempt to close and reopen log file
try:
if device.log_file:
device.log_file.close()
except:
pass
device.log_file = None
device.log_writer = None
device.is_recording = False
# Update UI if this is the active device
if device == self.active_device:
self.record_button.setChecked(False)
self.record_button.setText("Start Recording")
self.apply_button_style()
def adjust_downsampling(self):
current_length = len(self.time_data)