From e485697807936bd92f0b24676ec1971c69057ead Mon Sep 17 00:00:00 2001 From: "Jasper v. Blanckenburg" Date: Tue, 3 Jan 2023 22:57:18 +0100 Subject: [PATCH] Initial commit --- .gitignore | 4 ++ load_controller/__init__.py | 0 load_controller/gui.py | 124 ++++++++++++++++++++++++++++++++++++ load_controller/load.py | 19 ++++++ main.py | 24 +++++++ 5 files changed, 171 insertions(+) create mode 100644 .gitignore create mode 100644 load_controller/__init__.py create mode 100644 load_controller/gui.py create mode 100644 load_controller/load.py create mode 100755 main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2536221 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/env/ +/.venv/ +/.python-version +*.pyc diff --git a/load_controller/__init__.py b/load_controller/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/load_controller/gui.py b/load_controller/gui.py new file mode 100644 index 0000000..2bd8398 --- /dev/null +++ b/load_controller/gui.py @@ -0,0 +1,124 @@ +import enum +import os.path +import datetime +import csv + +from PyQt6.QtCore import QDir +from PyQt6.QtWidgets import ( + QWidget, + QGridLayout, + QHBoxLayout, + QVBoxLayout, + QLabel, + QPushButton, + QFileDialog, + QMessageBox, +) +from PyQt6.QtCharts import QChart, QChartView, QLineSeries + + +class ProfileState(enum.Enum): + STOPPED = 0 + RUNNING = 1 + PAUSED = 2 + + +class GUI(QWidget): + def __init__(self): + super().__init__(None) + + profile_label = QLabel("Profile:") + self.profile = QLabel("None") + self.profile.setStyleSheet("font-style: italic") + profile_chooser = QPushButton("Choose") + profile_chooser.clicked.connect(self._choose_profile) + profile_choice = QHBoxLayout() + profile_choice.addWidget(profile_label) + profile_choice.addWidget(self.profile) + profile_choice.addWidget(profile_chooser) + + chart_view = QChartView() + self.chart = QChart() + chart_view.setChart(self.chart) + + self.profile_state = ProfileState.STOPPED + self.start_pause = QPushButton("START") + self.start_pause.setDisabled(True) + self.start_pause.clicked.connect(self._start_pause) + self.stop = QPushButton("STOP") + self.stop.setDisabled(True) + self.stop.clicked.connect(self._stop) + profile_buttons = QHBoxLayout() + profile_buttons.addWidget(self.start_pause) + profile_buttons.addWidget(self.stop) + + profile_layout = QVBoxLayout() + profile_layout.addLayout(profile_choice) + profile_layout.addWidget(chart_view) + profile_layout.addLayout(profile_buttons) + + current_label = QLabel("Current [A]:") + self.current = QLabel("0") + time_label = QLabel("Time [min]:") + self.time = QLabel("00:00 / 00:00") + self.time.setStyleSheet("font-family: monospace") + voltage_label = QLabel("Voltage [V]:") + self.voltage = QLabel("[0.00, 0.00]") + temperature_label = QLabel("Temperature [°C]:") + self.temperature = QLabel("[00.0, 00.0]") + + info_layout = QGridLayout() + info_layout.addWidget(current_label, 0, 0) + info_layout.addWidget(self.current, 0, 1) + info_layout.addWidget(time_label, 1, 0) + info_layout.addWidget(self.time, 1, 1) + info_layout.addWidget(voltage_label, 2, 0) + info_layout.addWidget(self.voltage, 2, 1) + info_layout.addWidget(temperature_label, 3, 0) + info_layout.addWidget(self.temperature, 3, 1) + + layout = QHBoxLayout() + layout.addLayout(profile_layout) + layout.addLayout(info_layout) + + self.setLayout(layout) + self.setWindowTitle("Load Controller") + + def _choose_profile(self): + dlg = QFileDialog(parent=self) + dlg.setFileMode(QFileDialog.FileMode.ExistingFile) + dlg.setNameFilter("Profiles (*.csv)") + if not dlg.exec(): + return + + profile_path = dlg.selectedFiles()[0] + print(f"Profile: {profile_path}") + self.profile.setText(profile_path) + self.profile.setStyleSheet("font-style: normal") + + series = QLineSeries() + with open(profile_path) as fh: + t = 0 + for l in csv.reader(fh): + i = float(l[0]) + series.append(t, i) + t += float(l[1]) + series.append(t, i) + self.chart.removeAllSeries() + self.chart.addSeries(series) + self.chart.createDefaultAxes() + + def _start_pause(self): + if self.profile_state == ProfileState.RUNNING: + self.profile_state = ProfileState.PAUSED + self.start_pause.setText("RESUME") + else: + self.profile_state = ProfileState.RUNNING + self.start_pause.setText("PAUSE") + self.stop.setDisabled(False) + # TODO: Start logging if we were stopped + + def _stop(self): + self.profile_state = ProfileState.STOPPED + self.start_pause.setText("START") + self.stop.setDisabled(True) diff --git a/load_controller/load.py b/load_controller/load.py new file mode 100644 index 0000000..ce1fc39 --- /dev/null +++ b/load_controller/load.py @@ -0,0 +1,19 @@ +import serial +import time + +from PyQt6.QtCore import QObject, pyqtSignal + + +class Load(QObject): + dev: serial.Serial + finished: pyqtSignal + + def __init__(self, uart_path: str): + super(parent=None) + self.dev = serial.Serial(uart_path) + self.finished = pyqtSignal() + + def do_work(self): + while True: + # TODO + time.sleep(0.1) diff --git a/main.py b/main.py new file mode 100755 index 0000000..7896056 --- /dev/null +++ b/main.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +import os +import sys + +from PyQt6.QtWidgets import QApplication + +from load_controller.gui import GUI + + +def main(argv: list[str]) -> int: + app = QApplication(sys.argv) + gui = GUI() + gui.show() + + return app.exec() + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print(f"Usage: {sys.argv[0]} LOAD-PORT BMS-PORT", file=sys.stderr) + sys.exit(os.EX_USAGE) + + sys.exit(main(sys.argv))