fdr1gui/BlockStackApp.py
2025-04-14 13:19:49 +02:00

256 lines
9.0 KiB
Python

from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy, QPushButton
from PyQt5.QtCore import Qt
import glob
import requests
import serial
import re
import time
import math
from UpdateWorker import UpdateWorker
class BlockStackApp(QWidget):
def __init__(self, fullscreen):
super().__init__()
self.initUI(fullscreen)
self.check_usb_devices()
self.add_bottom_block()
self.update_count = 11
def initUI(self, fullscreen):
self.layout = QVBoxLayout()
self.layout.setSpacing(5)
self.layout.setContentsMargins(5, 5, 5, 5)
self.layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self.setLayout(self.layout)
self.setWindowTitle('462filament Bench GUI')
self.setStyleSheet("""
QWidget {
background-color: #2E2E2E;
color: #FFFFFF;
height: 100px;
}
QLabel {
background-color: #4A4A4A;
border: 1px solid #5A5A5A;
padding: 2px;
margin: 2px;
height: 30px;
width: 250px;
}
QPushButton, QComboBox {
background-color: #5A5A5A;
border: 1px solid #6A6A6A;
padding: 2px;
margin: 2px;
height: 25px;
width: 100px;
}
""")
if fullscreen:
self.showFullScreen()
else:
self.showMaximized()
def check_usb_devices(self):
bench_list = []
for port in glob.glob('/dev/ttyUSB*'):
try:
with serial.Serial(port, 1000000, timeout=1) as ser:
response = ser.read_all().decode('utf-8').strip()
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"Second check port {port}")
response = ser.read_all().decode('utf-8').strip()
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:
print(f"Could not open port {port}: {e}")
bench_list.sort(key=lambda x: x[1])
self.blocks = {}
for port, bench_id, serial_number in bench_list:
block_id = f"{bench_id}"
self.worker = UpdateWorker(serial_number, bench_id, port, block_id, self)
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
def extract_serial_number(self, response):
match = re.match(r"id:([0-9A-Fa-f]+)", response)
if match:
return match.group(1)
return None
def get_bench_id(self, serial_number):
url = f"http://462filament/api.php?data=benchid&serial={serial_number}"
try:
response = requests.get(url)
if response.status_code == 200:
data = response.json()
return data.get("id")
except requests.RequestException as e:
print(f"Error making request to API: {e}")
return None
def add_bottom_block(self):
# Create a new block for the bottom section
bottom_block_widget = QWidget()
bottom_block_layout = QHBoxLayout()
bottom_block_layout.setAlignment(Qt.AlignRight|Qt.AlignBottom)
bottom_block_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# Add a close button
btn_close = QPushButton("Close")
btn_close.clicked.connect(self.close)
bottom_block_layout.addWidget(btn_close)
bottom_block_widget.setLayout(bottom_block_layout)
self.layout.addWidget(bottom_block_widget)
def change_button_color(self, block_id, button_name, color):
block = self.blocks.get(block_id)
if block:
button = getattr(block, button_name, None)
if button:
button.setStyleSheet(f"background-color: {color};")
def update_mode(self, mode):
# 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")
def handle_data_received(self, data):
# Handle the data received from the serial port
print(f"Data received: {data}")
def update_block(self, is_running, diameter_value, position_value):
# Update the block with diameter_value and position_value
worker = self.sender()
diameter_avg = 2.0000
est_wgh = 0
worker.block.dia_label.setText(f"{diameter_value:.3f} ")
worker.block.len_label.setText(f"{position_value/1000:.3f}")
if is_running:
if worker.reset_data:
worker.diameter_total = diameter_value
worker.diameter_count = 1
worker.diameter_min = diameter_value
worker.diameter_max = diameter_value
worker.clear_graph()
worker.data_list.clear()
worker.reset_data = False
else:
worker.diameter_total += diameter_value # to calc average diameter
worker.diameter_count += 1
if diameter_value < worker.diameter_min: # process min/max
worker.diameter_min = diameter_value
if diameter_value > worker.diameter_max:
worker.diameter_max = diameter_value
worker.block.min_label.setText(f"{worker.diameter_min:.3f} ")
worker.block.max_label.setText(f"{worker.diameter_max:.3f} ")
# push data to memory for report
worker.data_list.append((position_value, diameter_value))
# push data to graph and update it
self.update_graph(worker, diameter_value)
# calculate and display average diameter
diameter_avg = worker.diameter_total / worker.diameter_count if worker.diameter_count > 0 else 0.0
worker.block.avg_label.setText(f"{diameter_avg:.4f}")
# calculate and display estimated weight
vol = math.pi * (diameter_avg / 2) ** 2 * position_value # all in mm → mm³
est_wgh = worker.density * vol/1000 # density in g·cm³, vol in mm³
worker.block.wht_label.setText(f"{est_wgh/1000:.3f} ")
def update_graph(self, worker, diameter_value):
# Update the graph with the new diameter value
x_data = worker.line.get_xdata()
y_data = worker.line.get_ydata()
x_data = list(x_data)
y_data = list(y_data)
# Append new data point
x_data.append(len(x_data))
y_data.append(diameter_value)
# Update the line with the new data
worker.line.set_data(x_data, y_data)
# Rescale the graph
worker.line.axes.relim()
worker.line.axes.autoscale_view()
# Redraw the canvas
worker.canvas.draw()