#!/usr/bin/env python3 import os import struct import time import random # import serial import sys from PyQt5.QtCore import Qt, QTimer from PyQt5.QtWidgets import ( QApplication, QWidget, QPushButton, QVBoxLayout, QHBoxLayout, QGridLayout, QSlider, QLabel, QRadioButton, QGroupBox, ) BITRATE = 115200 # baud/s TIMEOUT = 1 # seconds N_SLAVES = 6 LOG_FRAME_LENGTH = 8 # bytes CELLS_PER_SLAVE = 10 TEMP_SENSORS_PER_SLAVE = 32 VOLTAGE_CONV = 5.0 / 255 # volts/quantum TEMP_CONV = 0.0625 * 16 # °C/quantum class SlaveData: cell_voltages: list[float] cell_temps: list[float] def __init__(self) -> None: self.cell_voltages = [-1] * CELLS_PER_SLAVE self.cell_temps = [-1] * TEMP_SENSORS_PER_SLAVE class AccumulatorData: slaves: list[SlaveData] min_voltage: float max_voltage: float min_temp: float max_temp: float last_frame: float current: float panic: bool panic_errorcode: int panic_errorarg: int def __init__(self) -> None: self.slaves = [SlaveData() for _ in range(N_SLAVES)] self.min_voltage = ( self.max_voltage ) = self.min_temp = self.max_temp = self.last_frame = self.current = 0 self.panic = False self.panic_errorcode = self.panic_errorarg = 0 def fill_dummy_data(): data.min_voltage = random.uniform(1, 3) data.max_voltage = random.uniform(3, 5) data.min_temp = random.uniform(0, 25) data.max_temp = random.uniform(25, 60) data.current = random.uniform(25, 60) data.last_frame = time.time() - random.random() data.panic = random.choice([True, False]) if data.panic: data.panic_errorcode = random.randint(1, 10000) data.panic_errorarg = "ABCDERFG" else: data.panic_errorcode = 0 data.panic_errorarg = "" def update_display(): voltages = [ slave.cell_voltages[i] for i in range(CELLS_PER_SLAVE) for slave in data.slaves ] temps = [ slave.cell_temps[i] for i in range(TEMP_SENSORS_PER_SLAVE) for slave in data.slaves ] data.min_voltage = min(voltages) data.max_voltage = max(voltages) data.min_temp = min(temps) data.max_temp = max(temps) time_since_last_frame = time.time() - data.last_frame print("\033[2J\033[H", end="") print("-" * 20) if data.panic: print("!!!!! PANIC !!!!!") print(f"Error code: {data.panic_errorcode}") print(f"Error arg: {data.panic_errorarg}") time.sleep(0.5) return print(f"Time since last frame: {time_since_last_frame} s") print(f"Current: {data.current:.2f} A") print(f"Min voltage: {data.min_voltage:.2f} V") print(f"Max voltage: {data.max_voltage:.2f} V") print(f"Min temp: {data.min_temp:.1f} °C") print(f"Max temp: {data.max_temp:.1f} °C") for i in range(N_SLAVES): min_v = min(data.slaves[i].cell_voltages) max_v = max(data.slaves[i].cell_voltages) min_t = min(data.slaves[i].cell_temps) max_t = max(data.slaves[i].cell_temps) print( f"Stack {i}: V ∈ [{min_v:.2f}, {max_v:.2f}]\tT ∈ [{min_t:.1f}, {max_t:.1f}]" ) class Window(QWidget): def __init__(self, parent=None): super(Window, self).__init__(parent) win = QVBoxLayout() ### ACCUMULATOR GENERAL ### self.l1 = QLabel("Min Voltage [V]") self.l1.setAlignment(Qt.AlignLeft) self.l_min_voltage = QLabel() self.l_min_voltage.setNum(data.min_voltage) self.l_min_voltage.setAlignment(Qt.AlignLeft) self.l2 = QLabel("Max Voltage [V]") self.l2.setAlignment(Qt.AlignLeft) self.l_max_voltage = QLabel() self.l_max_voltage.setNum(data.max_voltage) self.l_max_voltage.setAlignment(Qt.AlignLeft) self.l3 = QLabel("Min Temperature [°C]") self.l3.setAlignment(Qt.AlignLeft) self.l_min_temp = QLabel() self.l_min_temp.setNum(data.min_temp) self.l_min_temp.setAlignment(Qt.AlignLeft) self.l4 = QLabel("Max Temperature [°C]") self.l4.setAlignment(Qt.AlignLeft) self.l_max_temp = QLabel() self.l_max_temp.setNum(data.max_temp) self.l_max_temp.setAlignment(Qt.AlignLeft) self.l5 = QLabel("Current [A]") self.l5.setAlignment(Qt.AlignLeft) self.l_current = QLabel() self.l_current.setNum(data.current) self.l_current.setAlignment(Qt.AlignLeft) self.l6 = QLabel("Error") self.l_error = QLabel() self.l_error.setText(str(data.panic)) self.l_error.setAlignment(Qt.AlignLeft) self.l_errorcode = QLabel() self.l_errorcode.setText(str(data.panic_errorcode)) self.l_errorcode.setAlignment(Qt.AlignLeft) self.l_errorarg = QLabel() self.l_errorarg.setText(str(data.panic_errorarg)) self.l_errorcode.setAlignment(Qt.AlignLeft) grid_accumulator = QGridLayout() grid_accumulator.addWidget(self.l1, 0, 0) grid_accumulator.addWidget(self.l2, 1, 0) grid_accumulator.addWidget(self.l3, 0, 2) grid_accumulator.addWidget(self.l4, 1, 2) grid_accumulator.addWidget(self.l5, 2, 0) grid_accumulator.addWidget(self.l6, 3, 0) grid_accumulator.addWidget(self.l_min_voltage, 0, 1) grid_accumulator.addWidget(self.l_max_voltage, 1, 1) grid_accumulator.addWidget(self.l_min_temp, 0, 3) grid_accumulator.addWidget(self.l_max_temp, 1, 3) grid_accumulator.addWidget(self.l_current, 2, 1) grid_accumulator.addWidget(self.l_error, 3, 1) grid_accumulator.addWidget(self.l_errorcode, 3, 2) grid_accumulator.addWidget(self.l_errorarg, 3, 3) groupBox_accumulator = QGroupBox("Accumulator General") groupBox_accumulator.setLayout(grid_accumulator) win.addWidget(groupBox_accumulator) ### STACKS ### ### STACK 0 ### self.l1_s0 = QLabel("Min Voltage [V]") self.l1_s0.setAlignment(Qt.AlignLeft) self.l_min_voltage_s0 = QLabel() self.l_min_voltage_s0.setNum(min(data.slaves[0].cell_voltages)) self.l_min_voltage_s0.setAlignment(Qt.AlignLeft) self.l2_s0 = QLabel("Max Voltage [V]") self.l2_s0.setAlignment(Qt.AlignLeft) self.l_max_voltage_s0 = QLabel() self.l_max_voltage_s0.setNum(max(data.slaves[0].cell_voltages)) self.l_max_voltage_s0.setAlignment(Qt.AlignLeft) self.l3_s0 = QLabel("Min Temperature [°C]") self.l3_s0.setAlignment(Qt.AlignLeft) self.l_min_temp_s0 = QLabel() self.l_min_temp_s0.setNum(min(data.slaves[0].cell_temps)) self.l_min_temp_s0.setAlignment(Qt.AlignLeft) self.l4_s0 = QLabel("Max Temperature [°C]") self.l4_s0.setAlignment(Qt.AlignLeft) self.l_max_temp_s0 = QLabel() self.l_max_temp_s0.setNum(max(data.slaves[0].cell_temps)) self.l_max_temp_s0.setAlignment(Qt.AlignLeft) grid_stack_s0 = QGridLayout() grid_stack_s0.addWidget(self.l1_s0, 0, 0) grid_stack_s0.addWidget(self.l2_s0, 1, 0) grid_stack_s0.addWidget(self.l3_s0, 0, 2) grid_stack_s0.addWidget(self.l4_s0, 1, 2) grid_stack_s0.addWidget(self.l_min_voltage_s0, 0, 1) grid_stack_s0.addWidget(self.l_max_voltage_s0, 1, 1) grid_stack_s0.addWidget(self.l_min_temp_s0, 0, 3) grid_stack_s0.addWidget(self.l_max_temp_s0, 1, 3) groupBox_stack_s0 = QGroupBox("Stack 0") groupBox_stack_s0.setLayout(grid_stack_s0) ### STACK 1 ### self.l1_s1 = QLabel("Min Voltage [V]") self.l1_s1.setAlignment(Qt.AlignLeft) self.l_min_voltage_s1 = QLabel() self.l_min_voltage_s1.setNum(min(data.slaves[1].cell_voltages)) self.l_min_voltage_s1.setAlignment(Qt.AlignLeft) self.l2_s1 = QLabel("Max Voltage [V]") self.l2_s1.setAlignment(Qt.AlignLeft) self.l_max_voltage_s1 = QLabel() self.l_max_voltage_s1.setNum(max(data.slaves[1].cell_voltages)) self.l_max_voltage_s1.setAlignment(Qt.AlignLeft) self.l3_s1 = QLabel("Min Temperature [°C]") self.l3_s1.setAlignment(Qt.AlignLeft) self.l_min_temp_s1 = QLabel() self.l_min_temp_s1.setNum(min(data.slaves[1].cell_temps)) self.l_min_temp_s1.setAlignment(Qt.AlignLeft) self.l4_s1 = QLabel("Max Temperature [°C]") self.l4_s1.setAlignment(Qt.AlignLeft) self.l_max_temp_s1 = QLabel() self.l_max_temp_s1.setNum(max(data.slaves[1].cell_temps)) self.l_max_temp_s1.setAlignment(Qt.AlignLeft) grid_stack_s1 = QGridLayout() grid_stack_s1.addWidget(self.l1_s1, 0, 0) grid_stack_s1.addWidget(self.l2_s1, 1, 0) grid_stack_s1.addWidget(self.l3_s1, 0, 2) grid_stack_s1.addWidget(self.l4_s1, 1, 2) grid_stack_s1.addWidget(self.l_min_voltage_s1, 0, 1) grid_stack_s1.addWidget(self.l_max_voltage_s1, 1, 1) grid_stack_s1.addWidget(self.l_min_temp_s1, 0, 3) grid_stack_s1.addWidget(self.l_max_temp_s1, 1, 3) groupBox_stack_s1 = QGroupBox("Stack 1") groupBox_stack_s1.setLayout(grid_stack_s1) ### Layout Stacks ### grid_stacks = QGridLayout() grid_stacks.addWidget(groupBox_stack_s0, 0, 0) grid_stacks.addWidget(groupBox_stack_s1, 0, 1) groupBox_stacks = QGroupBox("Individual Stacks") groupBox_stacks.setLayout(grid_stacks) win.addWidget(groupBox_stacks) self.setLayout(win) self.setWindowTitle("FT22 Charger Display") def update_gui(): # Accumulator gui.l_min_voltage.setNum(data.min_voltage) gui.l_max_voltage.setNum(data.max_voltage) gui.l_min_temp.setNum(data.min_temp) gui.l_max_temp.setNum(data.max_temp) gui.l_current.setNum(data.current) gui.l_error.setText(str(data.panic)) gui.l_errorcode.setText(str(data.panic_errorcode)) gui.l_errorarg.setText(str(data.panic_errorarg)) # Cells gui.l_min_voltage_s0.setNum(min(data.slaves[0].cell_voltages)) gui.l_max_voltage_s0.setNum(max(data.slaves[0].cell_voltages)) gui.l_min_temp_s0.setNum(min(data.slaves[0].cell_temps)) gui.l_max_temp_s0.setNum(max(data.slaves[0].cell_temps)) data = AccumulatorData() app = QApplication(sys.argv) gui = Window() gui.show() timer = QTimer() timer.timeout.connect(update_gui) timer.timeout.connect(fill_dummy_data) timer.start(1000) # every 1,000 milliseconds # update_display() sys.exit(app.exec_())