From a91f9d70db70d987d35c5752dbe31d00efa1b8f1 Mon Sep 17 00:00:00 2001 From: "Jasper v. Blanckenburg" Date: Thu, 26 Jan 2023 13:58:18 +0100 Subject: [PATCH] Check temperature & voltage --- load_controller/bms.py | 73 ++++++++++++++++++++++++++++++++++++++--- load_controller/gui.py | 1 + load_controller/load.py | 8 ++++- main.py | 2 +- ui/main_window.qml | 24 +++++++++++++- 5 files changed, 101 insertions(+), 7 deletions(-) diff --git a/load_controller/bms.py b/load_controller/bms.py index 7b200e9..211105c 100644 --- a/load_controller/bms.py +++ b/load_controller/bms.py @@ -1,7 +1,9 @@ from abc import abstractmethod import time from dataclasses import dataclass +from enum import Enum +from .load import Load from .temperatures import N_SENSORS from PySide6.QtCore import QObject, Signal, Slot, Qt @@ -15,16 +17,79 @@ SLAVE_UNIT_ID = 0x02 NUM_CELLS = 3 VOLTAGE_QUANT = 1 / 10000.0 +THRESH_UV = 2.5 +THRESH_OV = 4.2 +THRESH_UT = 1 +THRESH_OT = 60 + + +class BMSError(Enum): + NONE = 0 + UV = 1 + OV = 2 + UT = 3 + OT = 4 + + def __str__(self): + if self == self.NONE: + return "" + elif self == self.UV: + return "Undervoltage" + elif self == self.OV: + return "Overvoltage" + elif self == self.UT: + return "Undertemperature" + elif self == self.OT: + return "Overtemperature" + else: + return "Unknown error" + @dataclass class BMSData: voltages: list[float] temperatures: list[float] + error: BMSError class BMS(QObject): dataUpdated = Signal(BMSData) + _data: BMSData + _load: Load + _num_cells: int + _num_sensors: int + + def __init__(self, load: Load, num_cells: int, num_sensors: int): + super().__init__(None) + self._load = load + self._num_cells = num_cells + self._num_sensors = num_sensors + self._data = BMSData([0.0] * num_cells, [0.0] * num_sensors, BMSError.NONE) + + def _check_for_errors(self): + error = BMSError.NONE + for v in self._data.voltages: + if v > THRESH_OV: + error = BMSError.OV + break + elif v < THRESH_UV: + error = BMSError.UV + break + for t in self._data.temperatures: + if t > THRESH_OT: + error = BMSError.OT + break + if t < THRESH_UT: + error = BMSError.UT + break + + if error != self._data.error: + print(f"Error changed: {error}") + self._data.error = error + self._load.set_error(error != BMSError.NONE) + self.dataUpdated.emit(self._data) + @abstractmethod def do_work(self): pass @@ -32,14 +97,12 @@ class BMS(QObject): class BMSEvalBoard(BMS): _dev: ModbusSerialClient - _data: BMSData - def __init__(self, uart_path: str, temperaturesUpdated: Signal): - super().__init__(None) + def __init__(self, uart_path: str, temperaturesUpdated: Signal, load: Load): + super().__init__(load, NUM_CELLS, N_SENSORS) self._dev = ModbusSerialClient( method="rtu", port=uart_path, baudrate=MODBUS_BAUDRATE ) - self._data = BMSData([0.0] * NUM_CELLS, [0.0] * N_SENSORS) temperaturesUpdated.connect( self._updateTemperatures, Qt.ConnectionType.DirectConnection ) @@ -53,10 +116,12 @@ class BMSEvalBoard(BMS): self._data.voltages = list( map(lambda v: v * VOLTAGE_QUANT, result.registers) ) + self._check_for_errors() self.dataUpdated.emit(self._data) @Slot(list) def _updateTemperatures(self, temps: list[float]): assert len(temps) == N_SENSORS self._data.temperatures = temps + self._check_for_errors() self.dataUpdated.emit(self._data) diff --git a/load_controller/gui.py b/load_controller/gui.py index 05851d4..7ef72e8 100644 --- a/load_controller/gui.py +++ b/load_controller/gui.py @@ -88,3 +88,4 @@ class GUI(QObject): self._win.setProperty("voltage_max", max(data.voltages)) self._win.setProperty("temp_min", min(data.temperatures)) self._win.setProperty("temp_max", max(data.temperatures)) + self._win.setProperty("bmsError", str(data.error)) diff --git a/load_controller/load.py b/load_controller/load.py index c28c0d3..8f3ccb0 100644 --- a/load_controller/load.py +++ b/load_controller/load.py @@ -9,6 +9,7 @@ from .profile_handler import ProfileHandler class Load(QObject): dev: serial.Serial + _error: bool _current: float _timer: QTimer @@ -17,6 +18,7 @@ class Load(QObject): def __init__(self, uart_path: str, profile_handler: ProfileHandler): super().__init__(None) self.dev = serial.Serial(uart_path, 115200) + self._error = False self._current = 0 self._profile_handler = profile_handler @@ -25,12 +27,16 @@ class Load(QObject): self._timer.timeout.connect(self._update_load) self._timer.start(100) + def set_error(self, error): + self._error = error + @Slot() def _update_current(self, current): self._current = current def _update_load(self): - curr_quants = round(self._current / 0.1) + current = 0 if self._error else self._current + curr_quants = round(current / 0.1) assert curr_quants <= 0xFFFF msb = curr_quants >> 8 diff --git a/main.py b/main.py index fca318c..4adf064 100755 --- a/main.py +++ b/main.py @@ -23,7 +23,7 @@ def main(argv: list[str]) -> int: temps.moveToThread(temp_thread) temp_thread.started.connect(temps.run) temp_thread.start() - bms = BMSEvalBoard(argv[3], temps.temperaturesUpdated) + bms = BMSEvalBoard(argv[3], temps.temperaturesUpdated, load) bms_thread = QThread() bms.moveToThread(bms_thread) bms_thread.started.connect(bms.do_work) diff --git a/ui/main_window.qml b/ui/main_window.qml index 3d73e33..16bdc29 100644 --- a/ui/main_window.qml +++ b/ui/main_window.qml @@ -18,12 +18,13 @@ ApplicationWindow { property real voltage_max: 0 property real temp_min: 0 property real temp_max: 0 + property string bmsError: "" + property bool bmsErrorVisible: false Connections { target: profile_handler function onCurrentChanged(new_current) { - console.log("currentChanged"); current = new_current; } @@ -45,6 +46,11 @@ ApplicationWindow { currentTimeSeries.append(profileTime, 100) } + onBmsErrorChanged: { + console.log(bmsError); + bmsErrorVisible = bmsError != ""; + } + RowLayout { id: layout anchors.fill: parent @@ -151,6 +157,22 @@ ApplicationWindow { Layout.row: 3 Layout.column: 1 } + Text { + text: "BMS Error:" + Layout.row: 4 + Layout.column: 0 + visible: bmsErrorVisible + color: "red" + font.bold: true + } + Text { + text: bmsError + Layout.row: 4 + Layout.column: 1 + visible: bmsErrorVisible + color: "red" + font.bold: true + } } }