dev gui, mostly working

This commit is contained in:
Jonathan Roth 2025-04-07 15:56:20 +02:00
parent 3245233401
commit c82835cdb0

View File

@ -3,28 +3,234 @@ import glob
import serial import serial
import re import re
import requests import requests
import random
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout, from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QSizePolicy, QPushButton) QLabel, QSizePolicy, QPushButton)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSize from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSize
import time import time
class UpdateWorker(QThread): class UpdateWorker(QThread):
update_signal = pyqtSignal(int, int) update_signal = pyqtSignal(float, float, float, float)
data_received = pyqtSignal(str)
stop_signal = pyqtSignal()
not_homed_signal = pyqtSignal()
mode_signal = pyqtSignal(int)
change_color_signal = pyqtSignal(str, str, str) # Signal to change button color with identifier
def __init__(self, block, serial_number, bench_id): def __init__(self, serial_number, bench_id, port, block_id):
super().__init__() super().__init__()
self.block = block self.block = self.add_block(serial_number, bench_id, port, block_id)
self.serial_number = serial_number self.serial_number = serial_number
self.bench_id = bench_id self.bench_id = bench_id
self.position_value = 0 self.port = port
self.block_id = block_id # Unique identifier for the block
self.running = True
self.position_value = 0.0
self.diameter_value = 0.0
self.diameter_min = 0.0
self.diameter_max = 0.0
self.prepare_flag = False
self.prepare_ok = False
self.reset_flag = False
self.ignore_stop = False
def run(self): def run(self):
while True: try:
diameter_value = random.randint(0, 100) with serial.Serial(self.port, 1000000, timeout=1) as ser:
self.position_value += 1 ser.write(b'a') # Send the character 'F'
self.update_signal.emit(diameter_value, self.position_value) ser.write(b'F') # Send the character 'F
self.sleep(1) # Sleep for 1 second self.change_color_signal.emit(self.block_id, 'btn_home', "#A0A000")
while self.running:
if ser.in_waiting > 0:
data = ser.readline().decode('utf-8').strip()
if data:
self.parse_data(data)
if self.reset_flag and self.prepare_flag:
if self.position_value > 100 and self.reset_flag:
self.ignore_stop = True
self.change_color_signal.emit(self.block_id, 'btn_prepare', "#5A5A5A")
ser.write(b'R')
self.prepare_flag = False
if 1.65 < self.diameter_value < 2.99:
self.prepare_ok = True
else:
self.prepare_ok = False
if self.prepare_ok:
self.change_color_signal.emit(self.block_id, 'btn_prepare', "#00A000")
self.change_color_signal.emit(self.block_id, 'btn_start', "#A0A000")
else:
self.change_color_signal.emit(self.block_id, 'btn_prepare', "#A00000")
else:
self.change_color_signal.emit(self.block_id, 'btn_prepare', "#0000FF")
else:
if data == '%STOP':
if not self.ignore_stop :
self.change_color_signal.emit(self.block_id, 'btn_start', "#A00000")
else:
self.ignore_stop = False
elif data == '$NOTHOMED':
self.not_homed_signal.emit()
except serial.SerialException as e:
print(f"Error with port {self.port}: {e}")
def parse_data(self, data):
# Parse the data and update diameter_value and position_value
match = re.match(r"[#d]:([\d.-]+),p:([\d.-]+)", data)
if match:
self.diameter_value = float(match.group(1))
self.position_value = float(match.group(2))
if self.diameter_value < self.diameter_min:
self.diameter_min = self.diameter_value
if self.diameter_value > self.diameter_max:
self.diameter_max = self.diameter_value
self.update_signal.emit(self.diameter_value, self.position_value, self.diameter_min, self.diameter_max)
self.reset_flag = True
# Parse mode and position data
mode_match = re.match(r"#o:(\d+),l:(\d+),r:(\d+),c:(\d+)", data)
if mode_match:
mode = int(mode_match.group(1))
self.mode_signal.emit(mode)
match mode:
case 1:
self.change_color_signal.emit(self.block_id, 'btn_go_left', "#0000A0")
self.change_color_signal.emit(self.block_id, 'btn_go_right', "#5a5a5a")
self.change_color_signal.emit(self.block_id, 'btn_reset', "#5a5a5a")
case 2:
self.change_color_signal.emit(self.block_id, 'btn_go_left', "#5a5a5a")
self.change_color_signal.emit(self.block_id, 'btn_go_right', "#0000A0")
self.change_color_signal.emit(self.block_id, 'btn_reset', "#5a5a5a")
case 3:
self.change_color_signal.emit(self.block_id, 'btn_go_left', "#5a5a5a")
self.change_color_signal.emit(self.block_id, 'btn_go_right', "#5a5a5a")
self.change_color_signal.emit(self.block_id, 'btn_reset', "#00A0A0")
else:
self.mode_signal.emit(65535)
self.change_color_signal.emit(self.block_id, 'btn_go_left', "#5a5a5a")
self.change_color_signal.emit(self.block_id, 'btn_go_right', "#5a5a5a")
self.change_color_signal.emit(self.block_id, 'btn_reset', "#5A5A5A")
def send_command(self, command):
try:
with serial.Serial(self.port, 1000000, timeout=1) as ser:
ser.write(command.encode())
except serial.SerialException as e:
print(f"Could not send command to port {self.port}: {e}")
def set_prepare(self, prepare):
self.position_value = 0
self.prepare_flag = True
self.reset_flag = False
def reset_diameter_limits(self):
self.diameter_min = self.diameter_value
self.diameter_max = self.diameter_value
def add_block(self, serial_number, bench_id, port, block_id):
block_widget = QWidget()
block_layout = QHBoxLayout()
block_layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
LABEL_SIZE = QSize(250, 20)
BUTTON_SIZE = QSize(100, 25)
left_layout = QVBoxLayout()
data_label = QLabel("---")
bench_label = QLabel(f"Bench ID: {bench_id}")
bench_label.setFixedSize(LABEL_SIZE)
left_layout.addWidget(bench_label)
left_layout.addWidget(data_label)
center_layout = QVBoxLayout()
graph_placeholder = QLabel("Graph Placeholder")
graph_placeholder.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored)
center_layout.addWidget(graph_placeholder)
right_layout = QHBoxLayout()
right_layout_1 = QVBoxLayout()
right_layout_2 = QVBoxLayout()
right_layout_3 = QVBoxLayout()
btn_prepare = QPushButton("Prepare")
btn_prepare.clicked.connect(lambda: self.send_command('P'))
btn_prepare.clicked.connect(lambda: self.set_prepare(True))
right_layout_1.addWidget(btn_prepare)
btn_start = QPushButton("Start")
btn_start.clicked.connect(lambda: self.change_color_signal.emit(self.block_id, 'btn_start', "#00a000"))
btn_start.clicked.connect(lambda: self.change_color_signal.emit(self.block_id, 'btn_prepare', "#5A5A5A"))
btn_start.clicked.connect(self.reset_diameter_limits)
btn_start.clicked.connect(lambda: self.send_command('S'))
right_layout_1.addWidget(btn_start)
btn_reset = QPushButton("Reset/Zero")
btn_reset.clicked.connect(lambda: self.send_command('r'))
btn_reset.clicked.connect(lambda: self.change_color_signal.emit(self.block_id, 'btn_start', "#5A5A5A"))
right_layout_1.addWidget(btn_reset)
btn_stop = QPushButton("Stop")
btn_stop.clicked.connect(lambda: self.send_command('R'))
btn_stop.clicked.connect(lambda: self.change_color_signal.emit(self.block_id, 'btn_start', "#a00000"))
btn_stop.clicked.connect(lambda: self.change_color_signal.emit(self.block_id, 'btn_prepare', "#5A5A5A"))
right_layout_1.addWidget(btn_stop)
btn_sleep = QPushButton("Sleep")
btn_sleep.clicked.connect(lambda: self.send_command('a'))
btn_sleep.clicked.connect(lambda: self.change_color_signal.emit(self.block_id, 'btn_home', "#a0a000"))
right_layout_2.addWidget(btn_sleep)
btn_home = QPushButton("Home")
btn_home.clicked.connect(lambda: self.send_command(' '))
btn_home.clicked.connect(lambda: self.change_color_signal.emit(self.block_id, 'btn_home', "#5A5A5A"))
btn_home.clicked.connect(lambda: self.change_color_signal.emit(self.block_id, 'btn_start', "#5A5A5A"))
right_layout_2.addWidget(btn_home)
btn_go_left = QPushButton("Go Left")
btn_go_left.clicked.connect(lambda: self.send_command('1'))
right_layout_2.addWidget(btn_go_left)
btn_go_right = QPushButton("Go Right")
btn_go_right.clicked.connect(lambda: self.send_command('2'))
right_layout_2.addWidget(btn_go_right)
btn_down_left = QPushButton("Down Left")
btn_down_left.clicked.connect(lambda: self.send_command('g'))
right_layout_3.addWidget(btn_down_left)
btn_up_left = QPushButton("Up Left")
btn_up_left.clicked.connect(lambda: self.send_command('h'))
right_layout_3.addWidget(btn_up_left)
btn_down_right = QPushButton("Down Right")
btn_down_right.clicked.connect(lambda: self.send_command('t'))
right_layout_3.addWidget(btn_down_right)
btn_up_right = QPushButton("Up Right")
btn_up_right.clicked.connect(lambda: self.send_command('y'))
right_layout_3.addWidget(btn_up_right)
right_layout.addLayout(right_layout_1)
right_layout.addLayout(right_layout_2)
right_layout.addLayout(right_layout_3)
block_layout.addLayout(left_layout)
block_layout.addLayout(center_layout)
block_layout.addLayout(right_layout)
block_widget.setLayout(block_layout)
block_widget.btn_start = btn_start
block_widget.btn_home = btn_home
block_widget.btn_go_left = btn_go_left
block_widget.btn_go_right = btn_go_right
block_widget.btn_reset = btn_reset
block_widget.btn_prepare = btn_prepare
block_widget.data_label = data_label
return block_widget
class BlockStackApp(QWidget): class BlockStackApp(QWidget):
def __init__(self): def __init__(self):
@ -34,20 +240,14 @@ class BlockStackApp(QWidget):
self.check_usb_devices() self.check_usb_devices()
def initUI(self): def initUI(self):
# Set up the layout
self.layout = QVBoxLayout() self.layout = QVBoxLayout()
self.layout.setSpacing(5) # Set space between widgets to 5 pixels self.layout.setSpacing(5)
self.layout.setContentsMargins(5, 5, 5, 5) # Set margins around the layout to 5 pixels self.layout.setContentsMargins(5, 5, 5, 5)
# Align the layout to the left
self.layout.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
# Set the layout to the QWidget
self.setLayout(self.layout) self.setLayout(self.layout)
self.setWindowTitle('462filament Bench GUI') self.setWindowTitle('462filament Bench GUI')
# Apply dark theme
self.setStyleSheet(""" self.setStyleSheet("""
QWidget { QWidget {
background-color: #2E2E2E; background-color: #2E2E2E;
@ -72,38 +272,62 @@ class BlockStackApp(QWidget):
} }
""") """)
# Show the window maximized
self.showMaximized() self.showMaximized()
def check_usb_devices(self): def check_usb_devices(self):
# Check each /dev/ttyUSB? device bench_list = []
for port in glob.glob('/dev/ttyUSB*'): for port in glob.glob('/dev/ttyUSB*'):
try: try:
with serial.Serial(port, 1000000, timeout=1) as ser: with serial.Serial(port, 1000000, timeout=1) as ser:
ser.write(b'i') # Send the character 'i' response = ser.read_all().decode('utf-8').strip()
time.sleep(1) # Wait for 1 second to receive data ser.write(b'i')
time.sleep(1)
response = ser.read_all().decode('utf-8').strip() response = ser.read_all().decode('utf-8').strip()
serial_number = self.extract_serial_number(response) serial_number = self.extract_serial_number(response)
if serial_number: if serial_number:
print(f"Found bench on port {port}")
bench_id = self.get_bench_id(serial_number) bench_id = self.get_bench_id(serial_number)
if bench_id: if bench_id:
block = self.add_block(serial_number, bench_id) bench_list.append((port, bench_id, serial_number))
# Start a QThread for each block else:
self.worker = UpdateWorker(block, serial_number, bench_id) print(f"Second check port {port}")
self.worker.update_signal.connect(self.update_block) response = ser.read_all().decode('utf-8').strip()
self.worker.start() ser.write(b'i')
time.sleep(1)
response = ser.read_all().decode('utf-8').strip()
serial_number = self.extract_serial_number(response)
if serial_number:
print(f"Found bench on port {port}")
bench_id = self.get_bench_id(serial_number)
if bench_id:
bench_list.append((port, bench_id, serial_number))
else:
print(f"Ignoring port {port}")
except serial.SerialException as e: except serial.SerialException as e:
print(f"Could not open port {port}: {e}") print(f"Could not open port {port}: {e}")
bench_list.sort(key=lambda x: x[1])
self.blocks = {} # Dictionary to store blocks by block_id
for port, bench_id, serial_number in bench_list:
block_id = f"{bench_id}_{port}" # Unique identifier for the block
self.worker = UpdateWorker(serial_number, bench_id, port, block_id)
self.layout.addWidget(self.worker.block)
self.worker.change_color_signal.connect(self.change_button_color)
self.worker.update_signal.connect(self.update_block)
self.worker.start()
self.blocks[block_id] = self.worker.block # Store the block with its identifier
def extract_serial_number(self, response): def extract_serial_number(self, response):
# Extract the hexadecimal serial number using a regular expression
match = re.match(r"id:([0-9A-Fa-f]+)", response) match = re.match(r"id:([0-9A-Fa-f]+)", response)
if match: if match:
return match.group(1) return match.group(1)
return None return None
def get_bench_id(self, serial_number): def get_bench_id(self, serial_number):
# Make a request to the API to get the bench ID
url = f"http://462filament/api.php?data=benchid&serial={serial_number}" url = f"http://462filament/api.php?data=benchid&serial={serial_number}"
try: try:
response = requests.get(url) response = requests.get(url)
@ -114,86 +338,44 @@ class BlockStackApp(QWidget):
print(f"Error making request to API: {e}") print(f"Error making request to API: {e}")
return None return None
def add_block(self, serial_number, bench_id): def change_button_color(self, block_id, button_name, color):
# Create a new block with three zones block = self.blocks.get(block_id)
block_widget = QWidget() if block:
block_layout = QHBoxLayout() button = getattr(block, button_name, None)
block_layout.setAlignment(Qt.AlignLeft | Qt.AlignTop) if button:
button.setStyleSheet(f"background-color: {color};")
LABEL_SIZE = QSize(250, 20) def update_mode(self, mode):
BUTTON_SIZE = QSize(100, 25) # Update button colors based on mode
if mode == 1:
self.change_button_color(self.sender().block.btn_go_left, "#0000FF")
self.change_button_color(self.sender().block.btn_go_right, "#5A5A5A")
self.change_button_color(self.sender().block.btn_reset, "#5A5A5A")
elif mode == 2:
self.change_button_color(self.sender().block.btn_go_left, "#5A5A5A")
self.change_button_color(self.sender().block.btn_go_right, "#0000FF")
self.change_button_color(self.sender().block.btn_reset, "#5A5A5A")
elif mode == 65535:
self.change_button_color(self.sender().block.btn_go_left, "#5A5A5A")
self.change_button_color(self.sender().block.btn_go_right, "#5A5A5A")
self.change_button_color(self.sender().block.btn_reset, "#5A5A5A")
else:
self.change_button_color(self.sender().block.btn_go_left, "#5A5A5A")
self.change_button_color(self.sender().block.btn_go_right, "#5A5A5A")
self.change_button_color(self.sender().block.btn_reset, "#0000FF")
# Left zone def handle_data_received(self, data):
left_layout = QVBoxLayout() # Handle the data received from the serial port
data_label = QLabel(f"Est. weight: 0 g\nStop: at end") print(f"Data received: {data}")
bench_label = QLabel(f"Bench ID: {bench_id}")
bench_label.setFixedSize(LABEL_SIZE)
diameter_label = QLabel("Diameter: 0")
diameter_label.setFixedSize(LABEL_SIZE)
position_label = QLabel("Position: 0")
position_label.setFixedSize(LABEL_SIZE)
left_layout.addWidget(bench_label)
left_layout.addWidget(diameter_label)
left_layout.addWidget(position_label)
left_layout.addWidget(data_label)
# Center zone (placeholder for graph) def update_block(self, diameter_value, position_value, diameter_min, diameter_max):
center_layout = QVBoxLayout() # Update the block with diameter_value and position_value
graph_placeholder = QLabel("Graph Placeholder")
graph_placeholder.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored)
center_layout.addWidget(graph_placeholder)
# Right zone with buttons
right_layout = QHBoxLayout()
right_layout_1 = QVBoxLayout()
right_layout_2 = QVBoxLayout()
right_layout_3 = QVBoxLayout()
buttons = [
("Home", " "), ("Sleep", "a"), ("Down Right", "t"),
("Up Right", "y"), ("Down Left", "g"), ("Up Left", "h"),
("Start", "S"), ("Stop", "R"), ("Reset", "r"),
("Go Right", "1"), ("Go Left", "2")
]
btn_start = QPushButton(f"Start")
right_layout_1.addWidget(btn_start)
right_layout_1.addWidget(QPushButton(f"Reset"))
right_layout_1.addWidget(QPushButton(f"Stop"))
right_layout_1.addWidget(QPushButton(f"Home"))
right_layout_2.addWidget(QPushButton(f"Go Left"))
right_layout_2.addWidget(QPushButton(f""))
right_layout_2.addWidget(QPushButton(f"Go Right"))
right_layout_2.addWidget(QPushButton(f"Sleep"))
right_layout_3.addWidget(QPushButton(f"Down Left"))
right_layout_3.addWidget(QPushButton(f"Up Left"))
right_layout_3.addWidget(QPushButton(f"Down Right"))
right_layout_3.addWidget(QPushButton(f"Up Right"))
right_layout.addLayout(right_layout_1)
right_layout.addLayout(right_layout_2)
right_layout.addLayout(right_layout_3)
# Add zones to the block layout
block_layout.addLayout(left_layout)
block_layout.addLayout(center_layout)
block_layout.addLayout(right_layout)
block_widget.setLayout(block_layout)
self.layout.addWidget(block_widget)
# Store labels for updating
block_widget.diameter_label = diameter_label
block_widget.position_label = position_label
return block_widget
def update_block(self, diameter_value, position_value):
# Update the block with random value and incrementing position_value
worker = self.sender() worker = self.sender()
worker.block.diameter_label.setText(f"Diameter: {diameter_value}") worker.block.data_label.setText(f"⌀: {diameter_min:.3f} < ⌀̄ {diameter_value:.3f} mm < {diameter_max:.3f}\n"
worker.block.position_label.setText(f"Position: {position_value}") f"L: {position_value/1000:.4f} m\n"
f"ρ: {1} g·cm¯³\n"
"\n- - -\n\n"
f"M: 0 g\nStop: at end")
if __name__ == '__main__': if __name__ == '__main__':
app = QApplication(sys.argv) app = QApplication(sys.argv)