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)