Can you help me figure out why I don't see any lines plotting in this Python app?

from pid import SelfAdjustingPIDController
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.widgets import Slider, Button
from collections import deque
import time

class MockBattery:
    """
    Simulates a battery being charged in constant current mode.
    MV: Applied voltage
    PV: Charging current
    """
    def __init__(self, nominal_voltage=3.7, internal_resistance=0.1, 
                 capacity_ah=2.0, initial_soc=0.2):
        self.nominal_voltage = nominal_voltage
        self.internal_resistance = internal_resistance
        self.capacity_ah = capacity_ah
        self.soc = initial_soc  # State of charge (0-1)
        
        # Battery characteristics
        self.max_voltage = 4.2
        self.min_voltage = 3.0
        self.temperature = 25.0  # Celsius
        self.aging_factor = 1.0
        
    def get_ocv(self):
        """Open Circuit Voltage as function of SOC"""
        # Typical Li-ion OCV curve
        if self.soc < 0.1:
            ocv = 3.0 + 5 * self.soc
        elif self.soc < 0.9:
            ocv = 3.5 + 0.875 * (self.soc - 0.1)
        else:
            ocv = 4.2 - 0.5 * (1 - self.soc)
        return ocv
    
    def get_dynamic_resistance(self, current):
        """Dynamic resistance changes with current and SOC"""
        base_resistance = self.internal_resistance * self.aging_factor
        
        # Resistance increases at high/low SOC
        soc_factor = 1.0
        if self.soc < 0.1:
            soc_factor = 1 + 2 * (0.1 - self.soc)
        elif self.soc > 0.9:
            soc_factor = 1 + 3 * (self.soc - 0.9)
        
        # Resistance increases with current (polarization)
        current_factor = 1 + 0.1 * abs(current)
        
        # Temperature effect
        temp_factor = 1 + 0.02 * (25 - self.temperature)
        
        return base_resistance * soc_factor * current_factor * temp_factor
    
    def update(self, applied_voltage, dt):
        """
        Update battery state given applied voltage.
        Returns charging current.
        """
        # Get current OCV and resistance
        ocv = self.get_ocv()
        
        # Initial current estimate
        current = (applied_voltage - ocv) / self.internal_resistance
        
        # Iteratively solve for current with dynamic resistance
        for _ in range(3):
            r_dynamic = self.get_dynamic_resistance(current)
            current = (applied_voltage - ocv) / r_dynamic
        
        # Current limits
        max_current = 2.0 * self.capacity_ah  # 2C rate limit
        current = np.clip(current, 0, max_current)
        
        # Prevent overcharge
        if self.soc >= 0.99 and current > 0:
            current *= (1 - self.soc) * 100  # Taper current near full
        
        # Update SOC
        if current > 0:
            charge_added = current * dt / 3600  # Ah
            self.soc += charge_added / self.capacity_ah
            self.soc = min(1.0, self.soc)
        
        # Add measurement noise
        current += np.random.normal(0, 0.01)
        
        # Simulate temperature rise
        self.temperature += current * 0.1 * dt - (self.temperature - 25) * 0.01 * dt
        
        return max(0, current)

class MockChargeController:
    """Main controller that manages constant current charging"""
    def __init__(self, target_current=1.0, voltage_limits=(3.0, 4.3)):
        self.target_current = target_current
        self.voltage_min, self.voltage_max = voltage_limits
        
        # Initialize PID controller
        initial_voltage = (self.voltage_min + self.voltage_max) / 2
        self.pid = SelfAdjustingPIDController(
            mv_initial=initial_voltage,
            mv_min=self.voltage_min,
            mv_max=self.voltage_max
        )
        
        # Initialize battery model
        self.battery = MockBattery()
        
        # Data logging
        self.time_history = []
        self.voltage_history = []
        self.current_history = []
        self.soc_history = []
        self.temp_history = []
        
    def step(self, dt):
        """Execute one control step"""
        # Get current measurement from battery
        current = self.battery.update(self.pid.mv, dt)
        
        # PID control to maintain target current
        new_voltage = self.pid.get_new_mv(self.target_current, current)
        
        # Log data
        self.time_history.append(self.time_history[-1] + dt if self.time_history else 0)
        self.voltage_history.append(new_voltage)
        self.current_history.append(current)
        self.soc_history.append(self.battery.soc)
        self.temp_history.append(self.battery.temperature)
        
        return current, new_voltage, self.battery.soc
    
    def reset(self, target_current=None, initial_soc=0.0):
        """Reset controller and battery"""
        if target_current is not None:
            self.target_current = target_current
            
        initial_voltage = (self.voltage_min + self.voltage_max) / 2
        self.pid.reset(initial_voltage, self.voltage_min, self.voltage_max)
        self.battery = MockBattery(initial_soc=initial_soc)
        
        # Clear history
        self.time_history.clear()
        self.voltage_history.clear()
        self.current_history.clear()
        self.soc_history.clear()
        self.temp_history.clear()

# Visualization Application
class MockChargerVisualization:
    def __init__(self):
        self.controller = MockChargeController()
        self.setup_plots()
        self.setup_widgets()
        self.start_time = time.time()
        self.last_update = self.start_time
        
    def setup_plots(self):
        self.fig = plt.figure(figsize=(14, 10))
        gs = self.fig.add_gridspec(3, 2, hspace=0.3, wspace=0.3)
        
        # Main charging plot
        self.ax_main = self.fig.add_subplot(gs[0, :])
        self.line_current, = self.ax_main.plot([], [], 'b-', label='Current (A)')
        self.line_target, = self.ax_main.plot([], [], 'g--', label='Target Current')
        self.ax_voltage = self.ax_main.twinx()
        self.line_voltage, = self.ax_voltage.plot([], [], 'r-', label='Voltage (V)')
        
        self.ax_main.set_xlabel('Time (s)')
        self.ax_main.set_ylabel('Current (A)', color='b')
        self.ax_voltage.set_ylabel('Voltage (V)', color='r')
        self.ax_main.tick_params(axis='y', labelcolor='b')
        self.ax_voltage.tick_params(axis='y', labelcolor='r')
        self.ax_main.grid(True)
        self.ax_main.set_title('Battery Charging: Constant Current Control')
        
        # SOC and Temperature
        self.ax_soc = self.fig.add_subplot(gs[1, 0])
        self.line_soc, = self.ax_soc.plot([], [], 'purple', label='SOC')
        self.ax_soc.set_ylabel('State of Charge (%)')
        self.ax_soc.set_xlabel('Time (s)')
        self.ax_soc.grid(True)
        self.ax_soc.set_title('Battery State of Charge')
        
        self.ax_temp = self.fig.add_subplot(gs[1, 1])
        self.line_temp, = self.ax_temp.plot([], [], 'orange', label='Temperature')
        self.ax_temp.set_ylabel('Temperature (°C)')
        self.ax_temp.set_xlabel('Time (s)')
        self.ax_temp.grid(True)
        self.ax_temp.set_title('Battery Temperature')
        
        # PID gains
        self.ax_gains = self.fig.add_subplot(gs[2, :])
        self.line_kp, = self.ax_gains.plot([], [], 'r-', label='Kp')
        self.line_ki, = self.ax_gains.plot([], [], 'g-', label='Ki')
        self.line_kd, = self.ax_gains.plot([], [], 'b-', label='Kd')
        self.ax_gains.set_ylabel('Gain Value')
        self.ax_gains.set_xlabel('Time (s)')
        self.ax_gains.grid(True)
        self.ax_gains.legend()
        self.ax_gains.set_title('PID Controller Gains (Self-Adjusting)')
        
        # Data buffers
        self.max_points = 1000
        self.time_buffer = deque(maxlen=self.max_points)
        self.current_buffer = deque(maxlen=self.max_points)
        self.voltage_buffer = deque(maxlen=self.max_points)
        self.soc_buffer = deque(maxlen=self.max_points)
        self.temp_buffer = deque(maxlen=self.max_points)
        self.kp_buffer = deque(maxlen=self.max_points)
        self.ki_buffer = deque(maxlen=self.max_points)
        self.kd_buffer = deque(maxlen=self.max_points)
        
        # Add legends
        lines1, labels1 = self.ax_main.get_legend_handles_labels()
        lines2, labels2 = self.ax_voltage.get_legend_handles_labels()
        self.ax_main.legend(lines1 + lines2, labels1 + labels2, loc='upper left')
        
    def setup_widgets(self):
        plt.subplots_adjust(bottom=0.25)
        
        # Target current slider
        ax_current = plt.axes([0.15, 0.15, 0.7, 0.03])
        self.slider_current = Slider(ax_current, 'Target Current (A)', 
                                   0.1, 3.0, valinit=1.0)
        
        # Initial SOC slider
        ax_soc = plt.axes([0.15, 0.10, 0.7, 0.03])
        self.slider_soc = Slider(ax_soc, 'Initial SOC', 
                               0.1, 0.9, valinit=0.2)
        
        # Reset button
        ax_reset = plt.axes([0.8, 0.03, 0.15, 0.04])
        self.btn_reset = Button(ax_reset, 'Reset')
        self.btn_reset.on_clicked(self.reset_simulation)
        
        # Info text
        ax_info = plt.axes([0.05, 0.03, 0.7, 0.04])
        ax_info.axis('off')
        self.info_text = ax_info.text(0, 0.5, '', fontsize=10)
        
    def reset_simulation(self, event=None):
        self.controller.reset(
            target_current=self.slider_current.val,
            initial_soc=self.slider_soc.val
        )
        self.start_time = time.time()
        self.last_update = self.start_time
        
        # Clear buffers
        for buffer in [self.time_buffer, self.current_buffer, self.voltage_buffer,
                      self.soc_buffer, self.temp_buffer, self.kp_buffer, 
                      self.ki_buffer, self.kd_buffer]:
            buffer = deque(maxlen=self.max_points)
    
    def update(self, frame):
        current_time = time.time()
        dt = current_time - self.last_update
        self.last_update = current_time
        
        # Update controller
        self.controller.target_current = self.slider_current.val
        current, voltage, soc = self.controller.step(dt)
        
        # Update buffers
        elapsed = current_time - self.start_time
        self.time_buffer.append(elapsed)
        self.current_buffer.append(current)
        self.voltage_buffer.append(voltage)
        self.soc_buffer.append(soc * 100)
        self.temp_buffer.append(self.controller.battery.temperature)
        self.kp_buffer.append(self.controller.pid.Kp)
        self.ki_buffer.append(self.controller.pid.Ki)
        self.kd_buffer.append(self.controller.pid.Kd)
        
        # Update plots
        self.line_current.set_data(self.time_buffer, self.current_buffer)
        self.line_target.set_data(self.time_buffer, 
                                [self.controller.target_current] * len(self.time_buffer))
        self.line_voltage.set_data(self.time_buffer, self.voltage_buffer)
        self.line_soc.set_data(self.time_buffer, self.soc_buffer)
        self.line_temp.set_data(self.time_buffer, self.temp_buffer)
        self.line_kp.set_data(self.time_buffer, self.kp_buffer)
        self.line_ki.set_data(self.time_buffer, self.ki_buffer)
        self.line_kd.set_data(self.time_buffer, self.kd_buffer)
        
        # Update axes
        if self.time_buffer:
            for ax in [self.ax_main, self.ax_soc, self.ax_temp, self.ax_gains]:
                ax.set_xlim(0, current_time)
                ax.relim()
                ax.autoscale_view()
            self.ax_voltage.relim()
            self.ax_voltage.autoscale_view()
        
        # Update info text
        info = f"SOC: {soc*100:.1f}% | Current: {current:.2f}A | Voltage: {voltage:.2f}V | Temp: {self.controller.battery.temperature:.1f}°C"
        self.info_text.set_text(info)
        
        return (self.line_current, self.line_target, self.line_voltage,
                self.line_soc, self.line_temp, self.line_kp, self.line_ki, self.line_kd)
    
    def run(self):
        self.ani = FuncAnimation(self.fig, self.update, interval=100, blit=True)
        plt.show()

# Run the visualization
if __name__ == "__main__":
    app = MockChargerVisualization()
    app.run()