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:
parent
13148a64de
commit
f50a641211
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user