2026-01-07 11:47:54 +00:00
|
|
|
#!/user/bin/env python3
|
|
|
|
|
"""
|
|
|
|
|
AUTOMOTIVE POWER SIMULATOR APP - MAIN.PY
|
|
|
|
|
- ENTRY POINT OF APPLICATION
|
|
|
|
|
|
|
|
|
|
VERSION: 1.0
|
|
|
|
|
|
2026-01-07 16:01:52 +00:00
|
|
|
AUTHOR: D. RICE 06/01/2026
|
|
|
|
|
© 2026 ARRIVE
|
2026-01-07 11:47:54 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# Imports
|
|
|
|
|
import sys
|
|
|
|
|
from PySide6.QtWidgets import (QApplication, QDialog, QMainWindow)
|
|
|
|
|
from PySide6.QtCore import (QThread, QObject, Signal, Slot)
|
2026-01-07 16:01:52 +00:00
|
|
|
from PySide6.QtSerialPort import (QserialPort, QSerialPortInfo)
|
2026-01-07 11:47:54 +00:00
|
|
|
|
|
|
|
|
import time
|
2026-01-07 16:01:52 +00:00
|
|
|
import struct
|
2026-01-07 11:47:54 +00:00
|
|
|
|
|
|
|
|
from ui_mainwindow import Ui_MainWindow
|
|
|
|
|
from ui_scanning import Ui_scanningDialog
|
|
|
|
|
from ui_connerror import Ui_connerrorDialog
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Scanning Window Class
|
|
|
|
|
class ScanningWindow(QDialog):
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
# Initialise print window
|
|
|
|
|
super(ScanningWindow, self).__init__(parent)
|
|
|
|
|
# Finish print window initial setup
|
|
|
|
|
self.ui = Ui_scanningDialog()
|
|
|
|
|
self.ui.setupUi(self)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# No device warning Window Class
|
|
|
|
|
class NoDeviceWindow(QDialog):
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
# Initialise print window
|
|
|
|
|
super(NoDeviceWindow, self).__init__(parent)
|
|
|
|
|
# Finish print window initial setup
|
|
|
|
|
self.ui = Ui_connerrorDialog()
|
|
|
|
|
self.ui.setupUi(self)
|
|
|
|
|
|
|
|
|
|
# Static method to create the dialog and return response
|
|
|
|
|
@staticmethod
|
|
|
|
|
def get_response(parent=None):
|
|
|
|
|
dialog = NoDeviceWindow(parent)
|
|
|
|
|
results = dialog.exec()
|
|
|
|
|
return results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Main window class
|
|
|
|
|
class MainWindow(QMainWindow):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
# Initialise main window
|
|
|
|
|
super(MainWindow, self).__init__()
|
|
|
|
|
|
|
|
|
|
# Finish main window initial setup
|
|
|
|
|
self.ui = Ui_MainWindow()
|
|
|
|
|
self.ui.setupUi(self)
|
|
|
|
|
|
|
|
|
|
# Set up timer prototype
|
|
|
|
|
self.counter = 0
|
|
|
|
|
|
|
|
|
|
# Hold process reference.
|
|
|
|
|
self.p = None
|
|
|
|
|
|
|
|
|
|
# Thread and Worker initialisation
|
|
|
|
|
self.worker_thread = None
|
|
|
|
|
self.worker = None
|
|
|
|
|
|
|
|
|
|
# Add start button action
|
|
|
|
|
self.ui.connButton.setCheckable(True)
|
|
|
|
|
self.ui.connButton.clicked.connect(self.conn_button_press)
|
|
|
|
|
|
|
|
|
|
# Set initial size
|
2026-01-07 16:01:52 +00:00
|
|
|
initial_width = 1000
|
|
|
|
|
initial_height = 600
|
2026-01-07 11:47:54 +00:00
|
|
|
|
|
|
|
|
# Lock the window size by setting min/max to the same values
|
|
|
|
|
self.setMinimumSize(initial_width, initial_height)
|
|
|
|
|
self.setMaximumSize(initial_width, initial_height)
|
|
|
|
|
|
2026-01-07 16:01:52 +00:00
|
|
|
self.setGeometry(0, 0, 1000, 600)
|
2026-01-07 11:47:54 +00:00
|
|
|
|
2026-01-07 16:01:52 +00:00
|
|
|
self.setFixedSize(1000, 600)
|
2026-01-07 11:47:54 +00:00
|
|
|
|
|
|
|
|
self.show()
|
|
|
|
|
|
|
|
|
|
def conn_button_press (self):
|
|
|
|
|
global IDN_Response_flag
|
|
|
|
|
global Error_flag
|
|
|
|
|
|
|
|
|
|
text_conn = self.ui.connButton.text()
|
|
|
|
|
|
|
|
|
|
if text_conn == "CONNECT":
|
|
|
|
|
self.ui.connButton.setText("DISCONNECT")
|
|
|
|
|
|
|
|
|
|
w = ScanningWindow(self)
|
|
|
|
|
w.show()
|
|
|
|
|
|
|
|
|
|
QApplication.processEvents()
|
|
|
|
|
|
2026-01-07 16:01:52 +00:00
|
|
|
# Setup serial port
|
|
|
|
|
|
|
|
|
|
ser = self.serial.Serial(port="/dev/ttyACM0", baudrate=921600)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
command = 73 #0x49 'I' ASCII
|
|
|
|
|
state = 0
|
|
|
|
|
data = (command, state)
|
|
|
|
|
byte_data = self.pack_integers_to_bytes(*data)
|
|
|
|
|
|
2026-01-07 11:47:54 +00:00
|
|
|
|
|
|
|
|
while ((IDN_Response_flag == False) and (Error_flag == False)):
|
|
|
|
|
QApplication.processEvents()
|
|
|
|
|
|
|
|
|
|
if IDN_Response_flag == True:
|
|
|
|
|
|
|
|
|
|
w.close()
|
|
|
|
|
IDN_Response_flag = False
|
|
|
|
|
Error_flag = False
|
|
|
|
|
self.run_scope_setup()
|
|
|
|
|
|
|
|
|
|
if Error_flag == True:
|
|
|
|
|
response = NoDeviceWindow.get_response(self)
|
|
|
|
|
self.ui.connButton.setText("CONNECT")
|
|
|
|
|
self.closeEvent()
|
|
|
|
|
|
|
|
|
|
self.ui.manu.setText("---")
|
|
|
|
|
self.ui.model.setText("---")
|
|
|
|
|
self.ui.sn.setText("---")
|
|
|
|
|
self.ui.fw.setText("---")
|
|
|
|
|
|
|
|
|
|
IDN_Response_flag = False
|
|
|
|
|
Error_flag = False
|
|
|
|
|
|
|
|
|
|
QApplication.processEvents()
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
self.ui.connButton.setText("CONNECT")
|
|
|
|
|
self.closeEvent()
|
|
|
|
|
self.ui.manu.setText("---")
|
|
|
|
|
self.ui.model.setText("---")
|
|
|
|
|
self.ui.sn.setText("---")
|
|
|
|
|
self.ui.fw.setText("---")
|
|
|
|
|
|
|
|
|
|
QApplication.processEvents()
|
|
|
|
|
|
2026-01-07 16:01:52 +00:00
|
|
|
def pack_integers_to_bytes(self, *integers: int) -> bytes:
|
|
|
|
|
# Setup header
|
|
|
|
|
sync_bytes = [0x41, 0x52]
|
2026-01-07 11:47:54 +00:00
|
|
|
|
2026-01-07 16:01:52 +00:00
|
|
|
# Process data
|
|
|
|
|
# Mask each input to 8-bits to prevent packing errors
|
|
|
|
|
data_bytes = [i & 0xFF for i in integers]
|
2026-01-07 11:47:54 +00:00
|
|
|
|
2026-01-07 16:01:52 +00:00
|
|
|
# Get length
|
|
|
|
|
data_length = [len(data_bytes) & 0xFF]
|
2026-01-07 11:47:54 +00:00
|
|
|
|
2026-01-07 16:01:52 +00:00
|
|
|
# Calculate checksum
|
|
|
|
|
# Summingall data bytes, then applying bitwise NOT
|
|
|
|
|
total_sum = sum(data_bytes)
|
|
|
|
|
rx_checksum = ~total_sum
|
|
|
|
|
|
|
|
|
|
rx_checksum_h = (rx_checksum >> 8) & 0xFF
|
|
|
|
|
rx_checksum_l = rx_checksum & 0xFF
|
|
|
|
|
checksum_bytes = [rx_checksum_h,rx_checksum_l]
|
|
|
|
|
|
|
|
|
|
# Combine all components
|
|
|
|
|
full_packet = sync_bytes + data_length + data_bytes + checksum_bytes
|
|
|
|
|
|
|
|
|
|
# Dymanically create format string
|
|
|
|
|
# '!' = Network order, 'B' = unsigned char
|
|
|
|
|
# We need as many 'B's as there are elements in the full packet
|
|
|
|
|
format_string = f"!{len(full_packet)}B"
|
|
|
|
|
|
|
|
|
|
return struct.pack(format_string, *full_packet)
|
|
|
|
|
|
|
|
|
|
def run_scope_setup (self):
|
|
|
|
|
self.worker.run_setup()
|
|
|
|
|
|
2026-01-07 11:47:54 +00:00
|
|
|
|
|
|
|
|
# Run main
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
# Launch main window
|
|
|
|
|
app = QApplication(sys.argv)
|
|
|
|
|
app.setStyle('Fusion')
|
|
|
|
|
window = MainWindow()
|
|
|
|
|
window.show()
|
|
|
|
|
app.exec()
|
|
|
|
|
|
|
|
|
|
sys.exit()
|