From 39a7669fa76f575f92230ab48a38f4607fe00aec Mon Sep 17 00:00:00 2001 From: "f.geissler" Date: Mon, 18 Jul 2022 17:22:43 +0200 Subject: [PATCH] gui not working yet --- charger-display copy.py | 179 ++++++++++++++++++++++++++++++++++++++++ test.py | 113 +++++++++++++++++++++++-- 2 files changed, 284 insertions(+), 8 deletions(-) create mode 100755 charger-display copy.py diff --git a/charger-display copy.py b/charger-display copy.py new file mode 100755 index 0000000..4b73017 --- /dev/null +++ b/charger-display copy.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 + +import os +import struct +import time +import serial +import sys + +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 check_log_start(buf: bytes): + return buf[:-12].find(b"LOG") + + +def check_current_start(buf: bytes): + return buf[:-12].find(b"CUR") + + +def check_panic_start(buf: bytes): + return buf[:-12].find(b"PAN") + + +def decode_log_frame(buf: bytes): + msg_id = buf[0] + slave = msg_id >> 4 + frame_id = msg_id & 0x0F + if slave >= N_SLAVES: + return + + if frame_id == 0: + for i in range(7): + data.slaves[slave].cell_voltages[i] = buf[i + 1] * VOLTAGE_CONV + elif frame_id == 1: + for i in range(3): + data.slaves[slave].cell_voltages[i + 7] = buf[i + 1] * VOLTAGE_CONV + for i in range(4): + data.slaves[slave].cell_temps[i] = buf[i + 4] * TEMP_CONV + elif frame_id == 2: + for i in range(7): + data.slaves[slave].cell_temps[i + 4] = buf[i + 1] * TEMP_CONV + elif frame_id == 3: + for i in range(7): + data.slaves[slave].cell_temps[i + 11] = buf[i + 1] * TEMP_CONV + elif frame_id == 4: + for i in range(7): + data.slaves[slave].cell_temps[i + 18] = buf[i + 1] * TEMP_CONV + elif frame_id == 5: + for i in range(7): + data.slaves[slave].cell_temps[i + 25] = buf[i + 1] * TEMP_CONV + else: + # print(f"Unknown frame ID: {frame_id} (buf: {repr(buf)})", file=sys.stderr) + # time.sleep(1) + return + + data.last_frame = time.time() + + +def decode_current_frame(buf: bytes): + # current = (buf[2] << 24) | (buf[3] << 16) | (buf[4] << 8) | (buf[5]) + current = struct.unpack(">i", buf[2:6])[0] + data.current = current / 1000.0 + + +def decode_panic_frame(buf: bytes): + data.panic = True + data.panic_errorcode = buf[0] + data.panic_errorarg = buf[1] + + +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}]" + ) + + +if len(sys.argv) != 2: + print(f"Usage: {sys.argv[0]} SERIAL-PORT", file=sys.stderr) + sys.exit(os.EX_USAGE) + +SERIAL_PORT = sys.argv[1] +ser = serial.Serial(SERIAL_PORT, BITRATE, timeout=TIMEOUT) + +rx_buf = bytes() +data = AccumulatorData() + +while True: + rx_data = ser.read(32) + if len(rx_data) > 0: + rx_buf = rx_data + if (start := check_log_start(rx_buf)) != -1: + decode_log_frame(rx_buf[start + 3 : start + 11]) + rx_buf = b"" + elif (start := check_current_start(rx_buf)) != -1: + decode_current_frame(rx_buf[start + 3 : start + 11]) + rx_buf = b"" + elif (start := check_panic_start(rx_buf)) != -1: + decode_panic_frame(rx_buf[start + 3 : start + 11]) + rx_buf = b"" + + """ + if Button Charge is Pressed + print(f"KBHIT: {c}", file=sys.stderr) + print(ser.write(b"C"), file=sys.stderr) + """ + + update_display() diff --git a/test.py b/test.py index 09f4a53..4e0470b 100644 --- a/test.py +++ b/test.py @@ -4,10 +4,12 @@ import os import struct import time +import random + # import serial import sys -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, QTimer from PyQt5.QtWidgets import ( QApplication, QWidget, @@ -63,6 +65,22 @@ class AccumulatorData: 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 @@ -103,10 +121,6 @@ def update_display(): ) -def gui(): - pass - - class Window(QWidget): def __init__(self, parent=None): super(Window, self).__init__(parent) @@ -144,18 +158,33 @@ class Window(QWidget): 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) @@ -204,8 +233,50 @@ class Window(QWidget): 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) @@ -216,9 +287,35 @@ class Window(QWidget): 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() -update_display() app = QApplication(sys.argv) -clock = Window() -clock.show() +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_())