SoC for stacks
This commit is contained in:
		@ -54,6 +54,22 @@ MASTER_THRESH_OT = 1
 | 
				
			|||||||
MASTER_THRESH_UV = 2
 | 
					MASTER_THRESH_UV = 2
 | 
				
			||||||
MASTER_THRESH_OV = 3
 | 
					MASTER_THRESH_OV = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INTERNAL_RESISTANCE_CURVE_X = [2.0, 4.12]
 | 
				
			||||||
 | 
					INTERNAL_RESISTANCE_CURVE_Y = [0.0528, 0.0294]
 | 
				
			||||||
 | 
					SOC_OCV_X = [2.1, 2.9, 3.2, 3.3, 3.4, 3.5, 3.68, 4.0, 4.15, 4.2]
 | 
				
			||||||
 | 
					SOC_OCV_Y = [0, 0.023, 0.06, 0.08, 0.119, 0.227, 0.541, 0.856, 0.985, 1.0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def estimate_soc(voltage: float) -> float:
 | 
				
			||||||
 | 
					    r_i = np.interp(
 | 
				
			||||||
 | 
					        [voltage],
 | 
				
			||||||
 | 
					        INTERNAL_RESISTANCE_CURVE_X,
 | 
				
			||||||
 | 
					        INTERNAL_RESISTANCE_CURVE_Y,
 | 
				
			||||||
 | 
					    )[0]
 | 
				
			||||||
 | 
					    i = data.current / PARALLEL_CELLS
 | 
				
			||||||
 | 
					    ocv = voltage - i * r_i
 | 
				
			||||||
 | 
					    return np.interp([ocv], SOC_OCV_X, SOC_OCV_Y)[0] * 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SlaveData:
 | 
					class SlaveData:
 | 
				
			||||||
    cell_voltages: list[float]
 | 
					    cell_voltages: list[float]
 | 
				
			||||||
@ -135,63 +151,58 @@ class AccumulatorData:
 | 
				
			|||||||
class StackGuiElement:
 | 
					class StackGuiElement:
 | 
				
			||||||
    title: str
 | 
					    title: str
 | 
				
			||||||
    stack_id: int
 | 
					    stack_id: int
 | 
				
			||||||
    min_voltage_label: QLabel
 | 
					    voltage_label: QLabel
 | 
				
			||||||
    max_voltage_label: QLabel
 | 
					    soc_label: QLabel
 | 
				
			||||||
    min_temp_label: QLabel
 | 
					    temp_label: QLabel
 | 
				
			||||||
    max_temp_label: QLabel
 | 
					 | 
				
			||||||
    groupBox: QGroupBox
 | 
					    groupBox: QGroupBox
 | 
				
			||||||
    detail_popup: QWidget
 | 
					    detail_popup: QWidget
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, title: str, stack_id: int):
 | 
					    def __init__(self, title: str, stack_id: int):
 | 
				
			||||||
        self.title = title
 | 
					        self.title = title
 | 
				
			||||||
        self.stack_id = stack_id
 | 
					        self.stack_id = stack_id
 | 
				
			||||||
        self.min_voltage_label = QLabel()
 | 
					        self.voltage_label = QLabel()
 | 
				
			||||||
        self.max_voltage_label = QLabel()
 | 
					        self.soc_label = QLabel()
 | 
				
			||||||
        self.min_temp_label = QLabel()
 | 
					        self.temp_label = QLabel()
 | 
				
			||||||
        self.max_temp_label = QLabel()
 | 
					 | 
				
			||||||
        self.groupBox = QGroupBox()
 | 
					        self.groupBox = QGroupBox()
 | 
				
			||||||
        self.detail_popup = None
 | 
					        self.detail_popup = None
 | 
				
			||||||
        self.__post__init__()
 | 
					        self.__post__init__()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __post__init__(self):
 | 
					    def __post__init__(self):
 | 
				
			||||||
        l1 = QLabel("Min Voltage [V]")
 | 
					        l1 = QLabel("Voltage [V]")
 | 
				
			||||||
        l2 = QLabel("Max Voltage [V]")
 | 
					        l2 = QLabel("SoC [%]")
 | 
				
			||||||
        l3 = QLabel("Min Temperature [°C]")
 | 
					        l3 = QLabel("Temperature [°C]")
 | 
				
			||||||
        l4 = QLabel("Max Temperature [°C]")
 | 
					 | 
				
			||||||
        popup_btn = QPushButton("Details")
 | 
					        popup_btn = QPushButton("Details")
 | 
				
			||||||
        popup_btn.clicked.connect(self.show_popup)
 | 
					        popup_btn.clicked.connect(self.show_popup)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        l1.setAlignment(Qt.AlignLeft)
 | 
					        l1.setAlignment(Qt.AlignLeft)
 | 
				
			||||||
        l2.setAlignment(Qt.AlignLeft)
 | 
					        l2.setAlignment(Qt.AlignLeft)
 | 
				
			||||||
        l3.setAlignment(Qt.AlignLeft)
 | 
					        l3.setAlignment(Qt.AlignLeft)
 | 
				
			||||||
        l4.setAlignment(Qt.AlignLeft)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.min_voltage_label.setAlignment(Qt.AlignLeft)
 | 
					        self.voltage_label.setAlignment(Qt.AlignLeft)
 | 
				
			||||||
        self.max_voltage_label.setAlignment(Qt.AlignLeft)
 | 
					        self.soc_label.setAlignment(Qt.AlignLeft)
 | 
				
			||||||
        self.min_temp_label.setAlignment(Qt.AlignLeft)
 | 
					        self.temp_label.setAlignment(Qt.AlignLeft)
 | 
				
			||||||
        self.max_temp_label.setAlignment(Qt.AlignLeft)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        grid = QGridLayout()
 | 
					        grid = QGridLayout()
 | 
				
			||||||
        grid.addWidget(l1, 0, 0)
 | 
					        grid.addWidget(l1, 0, 0)
 | 
				
			||||||
        grid.addWidget(l2, 1, 0)
 | 
					        grid.addWidget(l2, 1, 0)
 | 
				
			||||||
        grid.addWidget(l3, 2, 0)
 | 
					        grid.addWidget(l3, 2, 0)
 | 
				
			||||||
        grid.addWidget(l4, 3, 0)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        grid.addWidget(self.min_voltage_label, 0, 1)
 | 
					        grid.addWidget(self.voltage_label, 0, 1)
 | 
				
			||||||
        grid.addWidget(self.max_voltage_label, 1, 1)
 | 
					        grid.addWidget(self.soc_label, 1, 1)
 | 
				
			||||||
        grid.addWidget(self.min_temp_label, 2, 1)
 | 
					        grid.addWidget(self.temp_label, 2, 1)
 | 
				
			||||||
        grid.addWidget(self.max_temp_label, 3, 1)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        grid.addWidget(popup_btn, 0, 2)
 | 
					        grid.addWidget(popup_btn, 3, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.groupBox.setTitle(self.title)
 | 
					        self.groupBox.setTitle(self.title)
 | 
				
			||||||
        self.groupBox.setLayout(grid)
 | 
					        self.groupBox.setLayout(grid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update_data_from_slave(self, slave: SlaveData):
 | 
					    def update_data_from_slave(self, slave: SlaveData):
 | 
				
			||||||
        self.min_voltage_label.setText(f"{min(slave.cell_voltages):.02f}")
 | 
					        min_v, max_v = min(slave.cell_voltages), max(slave.cell_voltages)
 | 
				
			||||||
        self.max_voltage_label.setText(f"{max(slave.cell_voltages):.02f}")
 | 
					        self.voltage_label.setText(f"[{min_v:.02f}, {max_v:.02f}]")
 | 
				
			||||||
        self.min_temp_label.setText(f"{min(slave.cell_temps):.02f}")
 | 
					        min_soc, max_soc = estimate_soc(min_v), estimate_soc(max_v)
 | 
				
			||||||
        self.max_temp_label.setText(f"{max(slave.cell_temps):.02f}")
 | 
					        self.soc_label.setText(f"[{min_soc:02.0f}, {max_soc:02.0f}]")
 | 
				
			||||||
 | 
					        min_t, max_t = min(slave.cell_temps), max(slave.cell_temps)
 | 
				
			||||||
 | 
					        self.temp_label.setText(f"[{min_t:.02f}, {max_t:.02f}]")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def show_popup(self):
 | 
					    def show_popup(self):
 | 
				
			||||||
        if self.detail_popup is None:
 | 
					        if self.detail_popup is None:
 | 
				
			||||||
@ -265,6 +276,7 @@ class StackPopup(QWidget):
 | 
				
			|||||||
        groupbox.setLayout(grid)
 | 
					        groupbox.setLayout(grid)
 | 
				
			||||||
        layout.addWidget(groupbox)
 | 
					        layout.addWidget(groupbox)
 | 
				
			||||||
        self.setLayout(layout)
 | 
					        self.setLayout(layout)
 | 
				
			||||||
 | 
					        self.setWindowTitle(f"FT22 Charger Display - Stack {stack_id} Details")
 | 
				
			||||||
        self.update_data()
 | 
					        self.update_data()
 | 
				
			||||||
        timer.timeout.connect(self.update_data)
 | 
					        timer.timeout.connect(self.update_data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -470,8 +482,8 @@ class Window(QWidget):
 | 
				
			|||||||
        # Accumulator
 | 
					        # Accumulator
 | 
				
			||||||
        self.l_min_voltage.setText(f"{data.min_voltage:.02f}")
 | 
					        self.l_min_voltage.setText(f"{data.min_voltage:.02f}")
 | 
				
			||||||
        self.l_max_voltage.setText(f"{data.max_voltage:.02f}")
 | 
					        self.l_max_voltage.setText(f"{data.max_voltage:.02f}")
 | 
				
			||||||
        self.l_min_soc.setText(f"{data.min_soc:.01f}")
 | 
					        self.l_min_soc.setText(f"{data.min_soc:02.0f}")
 | 
				
			||||||
        self.l_max_soc.setText(f"{data.max_soc:.01f}")
 | 
					        self.l_max_soc.setText(f"{data.max_soc:02.0f}")
 | 
				
			||||||
        self.l_min_temp.setText(f"{data.min_temp:.02f}")
 | 
					        self.l_min_temp.setText(f"{data.min_temp:.02f}")
 | 
				
			||||||
        self.l_max_temp.setText(f"{data.max_temp:.02f}")
 | 
					        self.l_max_temp.setText(f"{data.max_temp:.02f}")
 | 
				
			||||||
        self.l_current.setText(f"{data.current:.02f}")
 | 
					        self.l_current.setText(f"{data.current:.02f}")
 | 
				
			||||||
@ -612,8 +624,8 @@ class Worker(QObject):
 | 
				
			|||||||
        ]
 | 
					        ]
 | 
				
			||||||
        data.min_voltage = min(voltages)
 | 
					        data.min_voltage = min(voltages)
 | 
				
			||||||
        data.max_voltage = max(voltages)
 | 
					        data.max_voltage = max(voltages)
 | 
				
			||||||
        data.min_soc = self.calculate_soc(data.min_voltage)
 | 
					        data.min_soc = estimate_soc(data.min_voltage)
 | 
				
			||||||
        data.max_soc = self.calculate_soc(data.max_voltage)
 | 
					        data.max_soc = estimate_soc(data.max_voltage)
 | 
				
			||||||
        data.min_temp = min(temps)
 | 
					        data.min_temp = min(temps)
 | 
				
			||||||
        data.max_temp = max(temps)
 | 
					        data.max_temp = max(temps)
 | 
				
			||||||
        data.time_since_last_frame = time.time() - data.last_frame
 | 
					        data.time_since_last_frame = time.time() - data.last_frame
 | 
				
			||||||
@ -678,22 +690,6 @@ class Worker(QObject):
 | 
				
			|||||||
        data.ts_state = TSState(state)
 | 
					        data.ts_state = TSState(state)
 | 
				
			||||||
        data.panic = data.ts_state == TSState.TS_ERROR
 | 
					        data.panic = data.ts_state == TSState.TS_ERROR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    INTERNAL_RESISTANCE_CURVE_X = [2.0, 4.12]
 | 
					 | 
				
			||||||
    INTERNAL_RESISTANCE_CURVE_Y = [0.0528, 0.0294]
 | 
					 | 
				
			||||||
    SOC_OCV_X = [2.1, 2.9, 3.2, 3.3, 3.4, 3.5, 3.68, 4.0, 4.15, 4.2]
 | 
					 | 
				
			||||||
    SOC_OCV_Y = [0, 0.023, 0.06, 0.08, 0.119, 0.227, 0.541, 0.856, 0.985, 1.0]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def calculate_soc(self, voltage: float):
 | 
					 | 
				
			||||||
        r_i = np.interp(
 | 
					 | 
				
			||||||
            [voltage],
 | 
					 | 
				
			||||||
            self.INTERNAL_RESISTANCE_CURVE_X,
 | 
					 | 
				
			||||||
            self.INTERNAL_RESISTANCE_CURVE_Y,
 | 
					 | 
				
			||||||
        )[0]
 | 
					 | 
				
			||||||
        # i = data.current / PARALLEL_CELLS
 | 
					 | 
				
			||||||
        i = 3 / PARALLEL_CELLS
 | 
					 | 
				
			||||||
        ocv = voltage - i * r_i
 | 
					 | 
				
			||||||
        return np.interp([ocv], self.SOC_OCV_X, self.SOC_OCV_Y)[0] * 100
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def parse_cell_temps(self, slave: int):
 | 
					    def parse_cell_temps(self, slave: int):
 | 
				
			||||||
        temps = list(
 | 
					        temps = list(
 | 
				
			||||||
            filter(lambda t: t > 0 and t < 60, data.slaves[slave].cell_temps[:16])
 | 
					            filter(lambda t: t > 0 and t < 60, data.slaves[slave].cell_temps[:16])
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user