fdr1gui/fdr1gui.py

384 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import sys
import glob
import serial
import re
import requests
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QSizePolicy, QPushButton)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QSize
import time
class UpdateWorker(QThread):
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, serial_number, bench_id, port, block_id):
super().__init__()
self.block = self.add_block(serial_number, bench_id, port, block_id)
self.serial_number = serial_number
self.bench_id = bench_id
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):
try:
with serial.Serial(self.port, 1000000, timeout=1) as ser:
ser.write(b'a') # Send the character 'F'
ser.write(b'F') # Send the character 'F
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):
def __init__(self):
super().__init__()
self.initUI()
self.check_usb_devices()
def initUI(self):
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 {
background-color: #5A5A5A;
border: 1px solid #6A6A6A;
padding: 2px;
margin: 2px;
height: 30px;
width: 100px;
}
""")
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 = {} # 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):
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 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, diameter_value, position_value, diameter_min, diameter_max):
# Update the block with diameter_value and position_value
worker = self.sender()
worker.block.data_label.setText(f"⌀: {diameter_min:.3f} < ⌀̄ {diameter_value:.3f} mm < {diameter_max:.3f}\n"
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__':
app = QApplication(sys.argv)
ex = BlockStackApp()
sys.exit(app.exec_())