MainCode/adalm1000_logger.py aktualisiert
nearly working D
This commit is contained in:
parent
041bcc09a6
commit
cf6f91909f
@ -76,7 +76,7 @@ class DeviceManager:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def handle_read_error(self, increment=1):
|
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
|
self.consecutive_read_errors += increment
|
||||||
|
|
||||||
if self.consecutive_read_errors >= self.max_consecutive_errors:
|
if self.consecutive_read_errors >= self.max_consecutive_errors:
|
||||||
@ -117,7 +117,23 @@ class DeviceManager:
|
|||||||
# Add delay and attempt USB reset
|
# Add delay and attempt USB reset
|
||||||
time.sleep(2.0) # Longer delay for USB to settle
|
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
|
# Try multiple times to scan for devices
|
||||||
|
new_session = None
|
||||||
for attempt in range(3):
|
for attempt in range(3):
|
||||||
try:
|
try:
|
||||||
print(f"Scan attempt {attempt + 1}/3...")
|
print(f"Scan attempt {attempt + 1}/3...")
|
||||||
@ -130,27 +146,31 @@ class DeviceManager:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Find our device by serial
|
# Find our device by serial
|
||||||
for new_dev in devices:
|
new_dev = None
|
||||||
if new_dev.serial == self.serial:
|
for d in devices:
|
||||||
print(f"Found device {self.serial} on attempt {attempt+1}")
|
if d.serial == self.serial:
|
||||||
self.dev = new_dev
|
new_dev = d
|
||||||
self.consecutive_read_errors = 0
|
break
|
||||||
self.session = new_session # Update session reference
|
|
||||||
|
|
||||||
# Restart measurement thread
|
if new_dev:
|
||||||
if hasattr(self, 'measurement_thread'):
|
print(f"Found device {self.serial} on attempt {attempt+1}")
|
||||||
try:
|
self.dev = new_dev
|
||||||
self.measurement_thread.stop()
|
self.consecutive_read_errors = 0
|
||||||
time.sleep(0.5)
|
self.session = new_session # Update session reference
|
||||||
except:
|
|
||||||
pass
|
|
||||||
self.start_measurement(self.interval)
|
|
||||||
|
|
||||||
print("Device reinitialized successfully")
|
# Restart measurement thread
|
||||||
return True
|
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}")
|
print("Device reinitialized successfully")
|
||||||
time.sleep(1.0)
|
return True
|
||||||
|
else:
|
||||||
|
print(f"Original device not found on attempt {attempt+1}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Scan attempt {attempt+1} failed: {e}")
|
print(f"Scan attempt {attempt+1} failed: {e}")
|
||||||
@ -161,28 +181,84 @@ class DeviceManager:
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Full reinitialization failed: {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:
|
try:
|
||||||
print("Attempting USB port reset...")
|
# Linux-specific reset
|
||||||
import subprocess
|
result = subprocess.run(['usbreset', f'{vendor_id}:{product_id}'],
|
||||||
# ADALM1000 USB IDs
|
stdout=subprocess.PIPE,
|
||||||
result = subprocess.run(['usbreset', '064b:784c'],
|
stderr=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE,
|
text=True)
|
||||||
stderr=subprocess.PIPE)
|
|
||||||
if result.returncode == 0:
|
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)
|
time.sleep(3.0)
|
||||||
return self.handle_read_error(0) # Retry with counter reset
|
return self.handle_read_error(0) # Retry with counter reset
|
||||||
else:
|
else:
|
||||||
print("USB reset failed - command not found or permission denied")
|
print(f"USB reset command failed with code {result.returncode}")
|
||||||
except Exception as e:
|
print(f"Error output: {result.stderr}")
|
||||||
print(f"USB reset command failed: {e}")
|
|
||||||
|
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:
|
except Exception as e:
|
||||||
print(f"Device recovery failed: {e}")
|
print(f"USB reset command failed: {e}")
|
||||||
return False
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
return True # Not enough errors yet to trigger recovery
|
return True # Not enough errors yet to trigger recovery
|
||||||
|
|
||||||
@ -1127,37 +1203,29 @@ class BatteryTester(QMainWindow):
|
|||||||
|
|
||||||
def toggle_global_recording(self):
|
def toggle_global_recording(self):
|
||||||
"""Toggle recording for all connected devices simultaneously"""
|
"""Toggle recording for all connected devices simultaneously"""
|
||||||
if not hasattr(self, 'global_recording'):
|
|
||||||
self.global_recording = False
|
|
||||||
|
|
||||||
self.global_recording = not self.global_recording
|
self.global_recording = not self.global_recording
|
||||||
|
|
||||||
if self.global_recording:
|
if self.global_recording:
|
||||||
# Start recording for all devices
|
# Start recording for all devices
|
||||||
all_success = True
|
for device in self.devices.values():
|
||||||
for serial, device in self.devices.items():
|
device.is_recording = True
|
||||||
if not device.is_recording:
|
if not device.measurement_thread.isRunning():
|
||||||
if not self.start_live_monitoring(device):
|
device.start_measurement(self.interval)
|
||||||
all_success = False
|
self.start_live_monitoring(device)
|
||||||
print(f"Failed to start recording for device {serial}")
|
|
||||||
|
|
||||||
if all_success:
|
self.record_button.setText("■ Stop Recording")
|
||||||
self.record_button.setText("■ Stop Recording")
|
self.status_bar.showMessage("Recording started for all connected devices")
|
||||||
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")
|
|
||||||
else:
|
else:
|
||||||
# Stop recording for all devices
|
# Stop recording for all devices
|
||||||
for device in self.devices.values():
|
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.record_button.setText("● Start Recording")
|
||||||
self.apply_button_style() # Update style
|
|
||||||
self.status_bar.showMessage("Recording stopped for all devices")
|
self.status_bar.showMessage("Recording stopped for all devices")
|
||||||
|
|
||||||
|
self.apply_button_style()
|
||||||
|
|
||||||
def safe_execute(func):
|
def safe_execute(func):
|
||||||
"""Decorator to catch and log exceptions in Qt event handlers"""
|
"""Decorator to catch and log exceptions in Qt event handlers"""
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
@ -1380,13 +1448,18 @@ class BatteryTester(QMainWindow):
|
|||||||
self.handle_device_connection(False, "No devices found")
|
self.handle_device_connection(False, "No devices found")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.session.start(0)
|
|
||||||
self.devices = {}
|
self.devices = {}
|
||||||
|
self.active_device = None
|
||||||
|
|
||||||
for dev in self.session.devices:
|
for dev in self.session.devices:
|
||||||
manager = DeviceManager(dev) # Should work now
|
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
|
||||||
|
|
||||||
|
# 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
|
# Select first device
|
||||||
first_serial = next(iter(self.devices.keys()))
|
first_serial = next(iter(self.devices.keys()))
|
||||||
self.active_device = self.devices[first_serial]
|
self.active_device = self.devices[first_serial]
|
||||||
@ -1536,60 +1609,36 @@ class BatteryTester(QMainWindow):
|
|||||||
if serial not in self.devices:
|
if serial not in self.devices:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Stop current device
|
# Store current device state before switching
|
||||||
if self.active_device:
|
old_device = 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}")
|
|
||||||
|
|
||||||
# Activate new device
|
# Activate new device
|
||||||
self.active_device = self.devices[serial]
|
self.active_device = self.devices[serial]
|
||||||
|
|
||||||
# Ensure measurement thread exists and is properly configured
|
# Reconnect signals for new device
|
||||||
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
|
|
||||||
self.measurement_thread = self.active_device.measurement_thread
|
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.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)
|
||||||
|
|
||||||
# Ensure thread is running
|
# Restart measurement if not running
|
||||||
if not self.measurement_thread.isRunning():
|
if not self.measurement_thread.isRunning():
|
||||||
self.active_device.start_measurement(self.interval)
|
self.active_device.start_measurement(self.interval)
|
||||||
|
|
||||||
# Update UI with current device data
|
# Update UI with current device data
|
||||||
self.update_ui_from_active_device()
|
self.update_ui_from_active_device()
|
||||||
self.status_bar.showMessage(f"Switched to device: {serial}")
|
|
||||||
|
|
||||||
# Update recording button state
|
# Preserve recording state for old device
|
||||||
self.record_button.setChecked(self.global_recording)
|
if old_device and old_device.is_recording:
|
||||||
self.record_button.setText("Stop Recording" if self.global_recording else "Start Recording")
|
# Ensure old device continues recording
|
||||||
self.apply_button_style() # Ensure proper colors
|
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):
|
def update_ui_from_active_device(self):
|
||||||
dev = self.active_device
|
dev = self.active_device
|
||||||
@ -1622,52 +1671,77 @@ class BatteryTester(QMainWindow):
|
|||||||
self.phase_label.setText(dev.test_phase)
|
self.phase_label.setText(dev.test_phase)
|
||||||
|
|
||||||
@safe_execute
|
@safe_execute
|
||||||
@pyqtSlot(float, float, float)
|
@pyqtSlot(str, float, float, float)
|
||||||
def update_measurements(self, voltage, current, current_time):
|
def update_measurements(self, serial, voltage, current, current_time):
|
||||||
if not self.active_device:
|
"""Update measurements for a specific device identified by serial"""
|
||||||
|
# Get device from serial
|
||||||
|
device = self.devices.get(serial)
|
||||||
|
if not device:
|
||||||
return
|
return
|
||||||
|
|
||||||
dev = self.active_device
|
|
||||||
|
|
||||||
# Update device data
|
# Update device data
|
||||||
dev.time_data.append(current_time)
|
device.time_data.append(current_time)
|
||||||
dev.voltage_data.append(voltage)
|
device.voltage_data.append(voltage)
|
||||||
dev.current_data.append(current)
|
device.current_data.append(current)
|
||||||
|
|
||||||
# Calculate metrics
|
# Calculate metrics
|
||||||
power = voltage * abs(current) # Always define power
|
power = voltage * abs(current)
|
||||||
if len(dev.time_data) > 1:
|
if len(device.time_data) > 1:
|
||||||
delta_t = dev.time_data[-1] - dev.time_data[-2]
|
delta_t = device.time_data[-1] - device.time_data[-2]
|
||||||
dev.capacity_ah += abs(current) * delta_t / 3600 # Ah
|
device.capacity_ah += abs(current) * delta_t / 3600 # Ah
|
||||||
dev.energy += power * delta_t / 3600 # Wh
|
device.energy += power * delta_t / 3600 # Wh
|
||||||
|
|
||||||
# Update display buffers
|
# Update display buffers
|
||||||
dev.display_time_data.append(current_time)
|
device.display_time_data.append(current_time)
|
||||||
dev.display_voltage_data.append(voltage)
|
device.display_voltage_data.append(voltage)
|
||||||
dev.display_current_data.append(current)
|
device.display_current_data.append(current)
|
||||||
|
|
||||||
# Update UI for active device
|
# Update UI only if this is the active device
|
||||||
self.voltage_label.setText(f"{voltage:.4f}")
|
if device == self.active_device:
|
||||||
self.current_label.setText(f"{abs(current):.4f}")
|
self.voltage_label.setText(f"{voltage:.4f}")
|
||||||
self.capacity_label.setText(f"{dev.capacity_ah:.4f}")
|
self.current_label.setText(f"{abs(current):.4f}")
|
||||||
self.energy_label.setText(f"{dev.energy:.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()
|
now = time.time()
|
||||||
for device in self.devices.values():
|
if device.is_recording and device.log_writer and device.time_data:
|
||||||
if device.is_recording and device.log_writer and device.time_data:
|
if now - device._last_log_time >= 1.0: # Log at 1Hz
|
||||||
if now - device._last_log_time >= 1.0: # Log at 1Hz
|
try:
|
||||||
device.log_writer.writerow([
|
device.log_writer.writerow([
|
||||||
f"{current_time:.2f}",
|
f"{current_time:.2f}",
|
||||||
f"{voltage:.6f}",
|
f"{voltage:.6f}",
|
||||||
f"{current:.6f}",
|
f"{current:.6f}",
|
||||||
f"{dev.capacity_ah:.6f}",
|
f"{device.capacity_ah:.6f}",
|
||||||
f"{power:.6f}",
|
f"{power:.6f}",
|
||||||
f"{dev.energy:.6f}",
|
f"{device.energy:.6f}",
|
||||||
dev.test_phase
|
device.test_phase
|
||||||
])
|
])
|
||||||
device.log_file.flush()
|
device.log_file.flush()
|
||||||
device._last_log_time = now
|
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):
|
def adjust_downsampling(self):
|
||||||
current_length = len(self.time_data)
|
current_length = len(self.time_data)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user