From f50a641211e9f99fd3bb1c225967cca62827673f Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 24 May 2025 01:20:08 +0200 Subject: [PATCH] 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) --- MainCode/adalm1000_logger.py | 180 ++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 86 deletions(-) diff --git a/MainCode/adalm1000_logger.py b/MainCode/adalm1000_logger.py index c6f2e6a..ffc2153 100644 --- a/MainCode/adalm1000_logger.py +++ b/MainCode/adalm1000_logger.py @@ -433,13 +433,25 @@ class BatteryTester: return f"{hours:02d}:{minutes:02d}:{seconds:02d}" def stop_test(self): - """Request stop after current discharge phase completes""" + """Request immediate stop of the test""" if not self.test_running: return 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.start_button.config(state=tk.NORMAL) + + # Finalize test data + self.root.after(100, self.finalize_test) def center_window(self, window): """Center a window on screen""" @@ -455,12 +467,13 @@ class BatteryTester: test_current = self.c_rate.get() * self.capacity.get() 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 - - # 1. Charge (constant current) + + # 1. Charge phase (constant current) self.test_phase.set("Charge") 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.dev.channels['B'].mode = pysmu.Mode.HI_Z @@ -470,14 +483,13 @@ class BatteryTester: target_voltage = self.charge_cutoff.get() self.last_update_time = time.time() - while self.test_running: + while self.test_running and not self.request_stop: if not self.voltage_data: time.sleep(0.1) continue current_voltage = self.voltage_data[-1] measured_current = abs(self.current_data[-1]) - time_elapsed = time.time() - self.last_update_time # Update charge capacity now = time.time() @@ -488,17 +500,17 @@ class BatteryTester: self.status_var.set( f"Charging: {current_voltage:.3f}V / {target_voltage}V | " f"Current: {measured_current:.3f}A | " - f"Capacity: {self.charge_capacity.get():.4f}Ah | " - f"Time: {self.time_data[-1]:.1f}s" + f"Capacity: {self.charge_capacity.get():.4f}Ah" ) + self.root.update() # Force UI update - if current_voltage >= target_voltage: + if current_voltage >= target_voltage or self.request_stop: break - time.sleep(0.5) + time.sleep(0.1) # More frequent checks - if not self.test_running: - return + if self.request_stop or not self.test_running: + break # 2. Rest period after charge self.test_phase.set("Resting (Post-Charge)") @@ -507,21 +519,22 @@ class BatteryTester: self.dev.channels['A'].constant(0) 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()) self.status_var.set( f"Resting after charge | " - f"Time left: {time_left/60:.1f} min | " - f"Next: Final discharge to {self.discharge_cutoff.get()}V" + f"Time left: {time_left/60:.1f} min" ) - time.sleep(1) - - if not self.test_running: - return + self.root.update() + time.sleep(1) # Check every second for stop request + + if self.request_stop or not self.test_running: + break - # 3. Discharge (capacity measurement) + # 3. Discharge phase (capacity measurement) 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.dev.channels['A'].mode = pysmu.Mode.SIMV @@ -529,7 +542,7 @@ class BatteryTester: self.capacity_ah.set(0.0) self.last_update_time = time.time() - while self.test_running: + while self.test_running and not self.request_stop: if not self.current_data: time.sleep(0.1) continue @@ -537,105 +550,80 @@ class BatteryTester: current_voltage = self.voltage_data[-1] current_current = abs(self.current_data[-1]) - # Kapazitätsberechnung + # Capacity calculation now = time.time() delta_t = now - self.last_update_time self.last_update_time = now self.capacity_ah.set(self.capacity_ah.get() + current_current * delta_t / 3600) - # Statusupdate - status_msg = ( + self.status_var.set( f"Discharging: {current_voltage:.3f}V / {self.discharge_cutoff.get()}V | " f"Current: {current_current:.3f}A | " f"Capacity: {self.capacity_ah.get():.4f}Ah" ) - if self.request_stop: - status_msg += " | FINALIZING - completing discharge..." - self.status_var.set(status_msg) + self.root.update() if current_voltage <= self.discharge_cutoff.get() or self.request_stop: break - time.sleep(0.5) - - if self.request_stop: - time.sleep(0.5) - self.test_running = False - self.root.after(0, self.finalize_test) - return - - # 4. Rest period after charge - if self.test_running: + + time.sleep(0.1) # More frequent checks + + # 4. Rest period after discharge (only if not stopping) + if self.test_running and not self.request_stop: self.test_phase.set("Resting (Post-Discharge)") self.measuring = False self.dev.channels['A'].mode = pysmu.Mode.HI_Z self.dev.channels['A'].constant(0) 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()) self.status_var.set( f"Resting after discharge | " - f"Time left: {time_left/60:.1f} min | " - f"Next: Charge to {self.charge_cutoff.get()}V" + f"Time left: {time_left/60:.1f} min" ) + self.root.update() time.sleep(1) - - if not self.test_running: - return - - # Calculate Coulomb efficiency - if self.charge_capacity.get() > 0: + + # Calculate Coulomb efficiency if not stopping + if not self.request_stop and self.charge_capacity.get() > 0: efficiency = (self.capacity_ah.get() / self.charge_capacity.get()) * 100 self.coulomb_efficiency.set(efficiency) - - # Update GUI and show results - self.test_phase.set("Cycle Complete") - self.status_var.set( - f"Cycle {self.cycle_count.get()} complete | " - f"Discharge Capacity: {self.capacity_ah.get():.3f}Ah | " - f"Charge Capacity: {self.charge_capacity.get():.3f}Ah | " - f"Efficiency: {self.coulomb_efficiency.get():.1f}%" - ) - - # 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 - self.write_cycle_summary() - - # Reset capacities for next cycle - self.capacity_ah.set(0.0) - 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 + self.cycle_count.set(self.cycle_count.get() + 1) + + # Update cycle info + self.status_var.set( + f"Cycle {self.cycle_count.get()} complete | " + f"Discharge: {self.capacity_ah.get():.3f}Ah | " + f"Charge: {self.charge_capacity.get():.3f}Ah | " + f"Efficiency: {self.coulomb_efficiency.get():.1f}%" + ) + self.root.update() + + # Write cycle summary to log file + self.write_cycle_summary() + + # Short rest between cycles (only in continuous mode) + if self.continuous_mode and self.test_running and not self.request_stop: 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()) self.test_phase.set("Resting Between Cycles") self.status_var.set( f"Resting between cycles | " - f"Time left: {time_left/60:.1f} min | " - f"Next cycle will start soon" + f"Time left: {time_left/60:.1f} min" ) + self.root.update() time.sleep(1) - - # Automatically stop the test after completion if not in continuous mode - if not self.continuous_mode: - self.root.after(0, self.stop_test) - + + # Finalize test if stopped or completed + self.root.after(0, self.finalize_test) + except Exception as e: error_msg = str(e) if self.root.winfo_exists(): 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): """Final cleanup after test completes or is stopped""" @@ -682,6 +670,26 @@ class BatteryTester: self.voltage_label.config(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}" if not hasattr(self, '_last_current_text') or self._last_current_text != current_text: self.current_label.config(text=current_text)