MainCode/adalm1000_logger.py aktualisiert

Make all measurements update live in the GUI

    Allow the stop button to immediately halt the test at any point in the cycle

    Still maintain proper cleanup and data saving when stopped
(Deepseek)
This commit is contained in:
Jan 2025-05-24 01:20:08 +02:00
parent 13148a64de
commit f50a641211

View File

@ -433,13 +433,25 @@ class BatteryTester:
return f"{hours:02d}:{minutes:02d}:{seconds:02d}" return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
def stop_test(self): def stop_test(self):
"""Request stop after current discharge phase completes""" """Request immediate stop of the test"""
if not self.test_running: if not self.test_running:
return return
self.request_stop = True self.request_stop = True
self.status_var.set("Stop requested - will complete after current discharge phase") self.test_running = False # This will break out of all test loops
self.measuring = False
# Immediately set device to safe state
if hasattr(self, 'dev'):
self.dev.channels['A'].mode = pysmu.Mode.HI_Z
self.dev.channels['A'].constant(0)
self.status_var.set("Test stopped immediately")
self.stop_button.config(state=tk.DISABLED) self.stop_button.config(state=tk.DISABLED)
self.start_button.config(state=tk.NORMAL)
# Finalize test data
self.root.after(100, self.finalize_test)
def center_window(self, window): def center_window(self, window):
"""Center a window on screen""" """Center a window on screen"""
@ -455,12 +467,13 @@ class BatteryTester:
test_current = self.c_rate.get() * self.capacity.get() test_current = self.c_rate.get() * self.capacity.get()
while self.test_running and (self.continuous_mode or self.cycle_count.get() == 0): while self.test_running and (self.continuous_mode or self.cycle_count.get() == 0):
# Zurücksetzen des Stop-Requests zu Beginn jedes Zyklus # Reset stop request at start of each cycle
self.request_stop = False self.request_stop = False
# 1. Charge (constant current) # 1. Charge phase (constant current)
self.test_phase.set("Charge") self.test_phase.set("Charge")
self.status_var.set(f"Charging to {self.charge_cutoff.get()}V @ {test_current:.3f}A") self.status_var.set(f"Charging to {self.charge_cutoff.get()}V @ {test_current:.3f}A")
self.root.update() # Force UI update
self.measuring = True self.measuring = True
self.dev.channels['B'].mode = pysmu.Mode.HI_Z self.dev.channels['B'].mode = pysmu.Mode.HI_Z
@ -470,14 +483,13 @@ class BatteryTester:
target_voltage = self.charge_cutoff.get() target_voltage = self.charge_cutoff.get()
self.last_update_time = time.time() self.last_update_time = time.time()
while self.test_running: while self.test_running and not self.request_stop:
if not self.voltage_data: if not self.voltage_data:
time.sleep(0.1) time.sleep(0.1)
continue continue
current_voltage = self.voltage_data[-1] current_voltage = self.voltage_data[-1]
measured_current = abs(self.current_data[-1]) measured_current = abs(self.current_data[-1])
time_elapsed = time.time() - self.last_update_time
# Update charge capacity # Update charge capacity
now = time.time() now = time.time()
@ -488,17 +500,17 @@ class BatteryTester:
self.status_var.set( self.status_var.set(
f"Charging: {current_voltage:.3f}V / {target_voltage}V | " f"Charging: {current_voltage:.3f}V / {target_voltage}V | "
f"Current: {measured_current:.3f}A | " f"Current: {measured_current:.3f}A | "
f"Capacity: {self.charge_capacity.get():.4f}Ah | " f"Capacity: {self.charge_capacity.get():.4f}Ah"
f"Time: {self.time_data[-1]:.1f}s"
) )
self.root.update() # Force UI update
if current_voltage >= target_voltage: if current_voltage >= target_voltage or self.request_stop:
break break
time.sleep(0.5) time.sleep(0.1) # More frequent checks
if not self.test_running: if self.request_stop or not self.test_running:
return break
# 2. Rest period after charge # 2. Rest period after charge
self.test_phase.set("Resting (Post-Charge)") self.test_phase.set("Resting (Post-Charge)")
@ -507,21 +519,22 @@ class BatteryTester:
self.dev.channels['A'].constant(0) self.dev.channels['A'].constant(0)
rest_end_time = time.time() + (self.rest_time.get() * 3600) rest_end_time = time.time() + (self.rest_time.get() * 3600)
while time.time() < rest_end_time and self.test_running: while time.time() < rest_end_time and self.test_running and not self.request_stop:
time_left = max(0, rest_end_time - time.time()) time_left = max(0, rest_end_time - time.time())
self.status_var.set( self.status_var.set(
f"Resting after charge | " f"Resting after charge | "
f"Time left: {time_left/60:.1f} min | " f"Time left: {time_left/60:.1f} min"
f"Next: Final discharge to {self.discharge_cutoff.get()}V"
) )
time.sleep(1) self.root.update()
time.sleep(1) # Check every second for stop request
if not self.test_running: if self.request_stop or not self.test_running:
return break
# 3. Discharge (capacity measurement) # 3. Discharge phase (capacity measurement)
self.test_phase.set("Discharge") self.test_phase.set("Discharge")
self.status_var.set(f"discharge to {self.discharge_cutoff.get()}V @ {test_current:.3f}A") self.status_var.set(f"Discharging to {self.discharge_cutoff.get()}V @ {test_current:.3f}A")
self.root.update()
self.measuring = True self.measuring = True
self.dev.channels['A'].mode = pysmu.Mode.SIMV self.dev.channels['A'].mode = pysmu.Mode.SIMV
@ -529,7 +542,7 @@ class BatteryTester:
self.capacity_ah.set(0.0) self.capacity_ah.set(0.0)
self.last_update_time = time.time() self.last_update_time = time.time()
while self.test_running: while self.test_running and not self.request_stop:
if not self.current_data: if not self.current_data:
time.sleep(0.1) time.sleep(0.1)
continue continue
@ -537,105 +550,80 @@ class BatteryTester:
current_voltage = self.voltage_data[-1] current_voltage = self.voltage_data[-1]
current_current = abs(self.current_data[-1]) current_current = abs(self.current_data[-1])
# Kapazitätsberechnung # Capacity calculation
now = time.time() now = time.time()
delta_t = now - self.last_update_time delta_t = now - self.last_update_time
self.last_update_time = now self.last_update_time = now
self.capacity_ah.set(self.capacity_ah.get() + current_current * delta_t / 3600) self.capacity_ah.set(self.capacity_ah.get() + current_current * delta_t / 3600)
# Statusupdate self.status_var.set(
status_msg = (
f"Discharging: {current_voltage:.3f}V / {self.discharge_cutoff.get()}V | " f"Discharging: {current_voltage:.3f}V / {self.discharge_cutoff.get()}V | "
f"Current: {current_current:.3f}A | " f"Current: {current_current:.3f}A | "
f"Capacity: {self.capacity_ah.get():.4f}Ah" f"Capacity: {self.capacity_ah.get():.4f}Ah"
) )
if self.request_stop: self.root.update()
status_msg += " | FINALIZING - completing discharge..."
self.status_var.set(status_msg)
if current_voltage <= self.discharge_cutoff.get() or self.request_stop: if current_voltage <= self.discharge_cutoff.get() or self.request_stop:
break break
time.sleep(0.5)
if self.request_stop: time.sleep(0.1) # More frequent checks
time.sleep(0.5)
self.test_running = False
self.root.after(0, self.finalize_test)
return
# 4. Rest period after charge # 4. Rest period after discharge (only if not stopping)
if self.test_running: if self.test_running and not self.request_stop:
self.test_phase.set("Resting (Post-Discharge)") self.test_phase.set("Resting (Post-Discharge)")
self.measuring = False self.measuring = False
self.dev.channels['A'].mode = pysmu.Mode.HI_Z self.dev.channels['A'].mode = pysmu.Mode.HI_Z
self.dev.channels['A'].constant(0) self.dev.channels['A'].constant(0)
rest_end_time = time.time() + (self.rest_time.get() * 3600) rest_end_time = time.time() + (self.rest_time.get() * 3600)
while time.time() < rest_end_time and self.test_running: while time.time() < rest_end_time and self.test_running and not self.request_stop:
time_left = max(0, rest_end_time - time.time()) time_left = max(0, rest_end_time - time.time())
self.status_var.set( self.status_var.set(
f"Resting after discharge | " f"Resting after discharge | "
f"Time left: {time_left/60:.1f} min | " f"Time left: {time_left/60:.1f} min"
f"Next: Charge to {self.charge_cutoff.get()}V"
) )
self.root.update()
time.sleep(1) time.sleep(1)
if not self.test_running: # Calculate Coulomb efficiency if not stopping
return if not self.request_stop and self.charge_capacity.get() > 0:
# Calculate Coulomb efficiency
if self.charge_capacity.get() > 0:
efficiency = (self.capacity_ah.get() / self.charge_capacity.get()) * 100 efficiency = (self.capacity_ah.get() / self.charge_capacity.get()) * 100
self.coulomb_efficiency.set(efficiency) self.coulomb_efficiency.set(efficiency)
self.cycle_count.set(self.cycle_count.get() + 1)
# Update GUI and show results # Update cycle info
self.test_phase.set("Cycle Complete")
self.status_var.set( self.status_var.set(
f"Cycle {self.cycle_count.get()} complete | " f"Cycle {self.cycle_count.get()} complete | "
f"Discharge Capacity: {self.capacity_ah.get():.3f}Ah | " f"Discharge: {self.capacity_ah.get():.3f}Ah | "
f"Charge Capacity: {self.charge_capacity.get():.3f}Ah | " f"Charge: {self.charge_capacity.get():.3f}Ah | "
f"Efficiency: {self.coulomb_efficiency.get():.1f}%" f"Efficiency: {self.coulomb_efficiency.get():.1f}%"
) )
self.root.update()
# Show summary dialog only for the first cycle or when stopping
if not self.continuous_mode or not self.test_running:
self.root.after(0, lambda: messagebox.showinfo("Cycle Complete",
f"Cycle {self.cycle_count.get()} complete\n\n"
f"Discharge Capacity: {self.capacity_ah.get():.3f}Ah\n"
f"Charge Capacity: {self.charge_capacity.get():.3f}Ah\n"
f"Coulomb Efficiency: {self.coulomb_efficiency.get():.1f}%\n\n"
f"({self.capacity_ah.get()/self.capacity.get()*100:.1f}% of rated capacity)"))
# Write cycle summary to log file # Write cycle summary to log file
self.write_cycle_summary() self.write_cycle_summary()
# Reset capacities for next cycle # Short rest between cycles (only in continuous mode)
self.capacity_ah.set(0.0) if self.continuous_mode and self.test_running and not self.request_stop:
self.charge_capacity.set(0.0)
# Check if we should continue with another cycle
if self.continuous_mode and self.test_running:
# Short rest between cycles
rest_end_time = time.time() + (self.rest_time.get() * 3600) rest_end_time = time.time() + (self.rest_time.get() * 3600)
while time.time() < rest_end_time and self.test_running: while time.time() < rest_end_time and self.test_running and not self.request_stop:
time_left = max(0, rest_end_time - time.time()) time_left = max(0, rest_end_time - time.time())
self.test_phase.set("Resting Between Cycles") self.test_phase.set("Resting Between Cycles")
self.status_var.set( self.status_var.set(
f"Resting between cycles | " f"Resting between cycles | "
f"Time left: {time_left/60:.1f} min | " f"Time left: {time_left/60:.1f} min"
f"Next cycle will start soon"
) )
self.root.update()
time.sleep(1) time.sleep(1)
# Automatically stop the test after completion if not in continuous mode # Finalize test if stopped or completed
if not self.continuous_mode: self.root.after(0, self.finalize_test)
self.root.after(0, self.stop_test)
except Exception as e: except Exception as e:
error_msg = str(e) error_msg = str(e)
if self.root.winfo_exists(): if self.root.winfo_exists():
self.root.after(0, lambda msg=error_msg: messagebox.showerror("Test Error", msg)) self.root.after(0, lambda msg=error_msg: messagebox.showerror("Test Error", msg))
self.root.after(0, self.stop_test) self.root.after(0, self.finalize_test)
def finalize_test(self): def finalize_test(self):
"""Final cleanup after test completes or is stopped""" """Final cleanup after test completes or is stopped"""
@ -682,6 +670,26 @@ class BatteryTester:
self.voltage_label.config(text=voltage_text) self.voltage_label.config(text=voltage_text)
self._last_voltage_text = voltage_text self._last_voltage_text = voltage_text
capacity_text = f"{self.capacity_ah.get():.4f}"
if not hasattr(self, '_last_capacity_text') or self._last_capacity_text != capacity_text:
self.capacity_label.config(text=capacity_text)
self._last_capacity_text = capacity_text
charge_capacity_text = f"{self.charge_capacity.get():.4f}"
if not hasattr(self, '_last_charge_capacity_text') or self._last_charge_capacity_text != charge_capacity_text:
self.charge_capacity_label.config(text=charge_capacity_text)
self._last_charge_capacity_text = charge_capacity_text
efficiency_text = f"{self.coulomb_efficiency.get():.1f}"
if not hasattr(self, '_last_efficiency_text') or self._last_efficiency_text != efficiency_text:
self.efficiency_label.config(text=efficiency_text)
self._last_efficiency_text = efficiency_text
cycle_text = f"{self.cycle_count.get()}"
if not hasattr(self, '_last_cycle_text') or self._last_cycle_text != cycle_text:
self.cycle_label.config(text=cycle_text)
self._last_cycle_text = cycle_text
current_text = f"{current:.4f}" current_text = f"{current:.4f}"
if not hasattr(self, '_last_current_text') or self._last_current_text != current_text: if not hasattr(self, '_last_current_text') or self._last_current_text != current_text:
self.current_label.config(text=current_text) self.current_label.config(text=current_text)