194 lines
7.3 KiB
Python
Executable File
194 lines
7.3 KiB
Python
Executable File
"""
|
|
Genesis Laser Worker Thread
|
|
|
|
Manages Genesis laser connection in a separate thread to keep the UI responsive.
|
|
Provides async querying and status monitoring via Qt signals.
|
|
"""
|
|
|
|
from PyQt6 import QtCore
|
|
from hardware.genesis_core import SerialComm, I2CProtocol, I2CDevices, LaserControl
|
|
import queue
|
|
import time
|
|
from typing import Optional
|
|
|
|
|
|
class GenesisCommand:
|
|
"""Represents a genesis laser command"""
|
|
def __init__(self, cmd_type: str, **kwargs):
|
|
self.cmd_type = cmd_type
|
|
self.params = kwargs
|
|
|
|
|
|
class GenesisWorker(QtCore.QObject):
|
|
"""
|
|
Worker object for handling Genesis laser control in a separate thread.
|
|
|
|
Signals:
|
|
connected: Emitted when laser connects successfully
|
|
disconnected: Emitted when laser disconnects
|
|
connection_failed: Emitted when connection fails (error_msg: str)
|
|
laser_info_updated: Emitted with laser status
|
|
query_completed: Emitted when a query operation completes (result: dict)
|
|
error_occurred: Emitted when an error occurs (error_msg: str)
|
|
"""
|
|
|
|
# Signals
|
|
connected = QtCore.pyqtSignal()
|
|
disconnected = QtCore.pyqtSignal()
|
|
connection_failed = QtCore.pyqtSignal(str)
|
|
laser_info_updated = QtCore.pyqtSignal(dict) # Status information
|
|
query_completed = QtCore.pyqtSignal(dict) # Query result
|
|
error_occurred = QtCore.pyqtSignal(str) # Error message
|
|
|
|
def __init__(self, port: str = "/dev/ttyUSB0", baudrate: int = 9600):
|
|
super().__init__()
|
|
self.serial_comm = SerialComm()
|
|
self.i2c_protocol = I2CProtocol(self.serial_comm)
|
|
self.i2c_devices = I2CDevices(self.i2c_protocol)
|
|
self.laser_control = LaserControl(self.i2c_devices)
|
|
|
|
self.port = port
|
|
self.baudrate = baudrate
|
|
self.is_connected = False
|
|
self.command_queue = queue.Queue()
|
|
self.running = True
|
|
|
|
# Last known laser state
|
|
self.last_laser_state = {}
|
|
|
|
# Update interval for status polling
|
|
self.last_status_update_time = 0
|
|
self.status_update_interval = 1.0 # seconds
|
|
|
|
@QtCore.pyqtSlot()
|
|
def run(self):
|
|
"""Main worker loop - processes commands from queue"""
|
|
print(f"Genesis laser worker thread started - connecting to {self.port}")
|
|
|
|
# Try to connect on startup
|
|
if self.connect():
|
|
self.connected.emit()
|
|
else:
|
|
error_msg = f"Failed to connect to Genesis laser on {self.port}"
|
|
print(error_msg)
|
|
self.connection_failed.emit(error_msg)
|
|
|
|
while self.running:
|
|
try:
|
|
# Check for commands with timeout to allow periodic status updates
|
|
try:
|
|
cmd = self.command_queue.get(timeout=0.05) # 50ms timeout
|
|
self.process_command(cmd)
|
|
except queue.Empty:
|
|
pass
|
|
|
|
# Periodically update status if connected
|
|
if self.is_connected:
|
|
current_time = time.time()
|
|
if current_time - self.last_status_update_time >= self.status_update_interval:
|
|
self.update_laser_status()
|
|
self.last_status_update_time = current_time
|
|
|
|
except Exception as e:
|
|
print(f"Error in genesis worker loop: {e}")
|
|
self.error_occurred.emit(str(e))
|
|
|
|
# Cleanup on exit
|
|
self.disconnect()
|
|
print("Genesis laser worker thread stopped")
|
|
|
|
def connect(self) -> bool:
|
|
"""Establish connection to the laser"""
|
|
try:
|
|
if self.serial_comm.connect(self.port, self.baudrate):
|
|
self.is_connected = True
|
|
print(f"Connected to Genesis laser on {self.port}")
|
|
return True
|
|
else:
|
|
print(f"Failed to open serial port {self.port}")
|
|
return False
|
|
except Exception as e:
|
|
print(f"Connection error: {e}")
|
|
return False
|
|
|
|
def disconnect(self):
|
|
"""Disconnect from the laser"""
|
|
if self.is_connected:
|
|
self.serial_comm.disconnect()
|
|
self.is_connected = False
|
|
self.disconnected.emit()
|
|
print("Disconnected from Genesis laser")
|
|
|
|
def process_command(self, cmd: GenesisCommand):
|
|
"""Process a command from the queue"""
|
|
if not self.is_connected:
|
|
self.error_occurred.emit("Laser not connected")
|
|
return
|
|
|
|
try:
|
|
if cmd.cmd_type == "query_all":
|
|
result = self.query_all_status()
|
|
self.query_completed.emit(result)
|
|
elif cmd.cmd_type == "query_current":
|
|
result = {"current": self.laser_control.get_current_actual()}
|
|
self.query_completed.emit(result)
|
|
elif cmd.cmd_type == "query_interlock":
|
|
result = {"interlock": self.laser_control.get_interlock_status()}
|
|
self.query_completed.emit(result)
|
|
elif cmd.cmd_type == "set_current":
|
|
value = cmd.params.get("value", 0)
|
|
success = self.laser_control.set_current(int(value))
|
|
self.query_completed.emit({"success": success})
|
|
elif cmd.cmd_type == "set_shutter":
|
|
state = cmd.params.get("state", False)
|
|
success = self.laser_control.set_shutter(state)
|
|
self.query_completed.emit({"success": success})
|
|
else:
|
|
self.error_occurred.emit(f"Unknown command: {cmd.cmd_type}")
|
|
except Exception as e:
|
|
self.error_occurred.emit(f"Command execution error: {e}")
|
|
|
|
def update_laser_status(self):
|
|
"""Query and emit current laser status"""
|
|
if not self.is_connected:
|
|
return
|
|
|
|
try:
|
|
status = {
|
|
"connected": True,
|
|
"current_actual": self.laser_control.get_current_actual(),
|
|
"interlock_status": self.laser_control.get_interlock_status(),
|
|
"ldd_enable_status": self.laser_control.get_ldd_enable_status(),
|
|
"psglue_in_status": self.laser_control.get_psglue_in_status(),
|
|
"psglue_out_status": self.laser_control.get_psglue_out_status(),
|
|
"head_dio_status": self.laser_control.get_head_dio_status(),
|
|
}
|
|
|
|
# Only emit if something changed
|
|
if status != self.last_laser_state:
|
|
self.last_laser_state = status
|
|
self.laser_info_updated.emit(status)
|
|
|
|
except Exception as e:
|
|
print(f"Error updating laser status: {e}")
|
|
|
|
def query_all_status(self) -> dict:
|
|
"""Query all laser status information"""
|
|
return {
|
|
"connected": True,
|
|
"current_actual": self.laser_control.get_current_actual(),
|
|
"interlock_status": self.laser_control.get_interlock_status(),
|
|
"ldd_enable_status": self.laser_control.get_ldd_enable_status(),
|
|
"psglue_in_status": self.laser_control.get_psglue_in_status(),
|
|
"psglue_out_status": self.laser_control.get_psglue_out_status(),
|
|
"head_dio_status": self.laser_control.get_head_dio_status(),
|
|
}
|
|
|
|
def queue_command(self, cmd: GenesisCommand):
|
|
"""Queue a command for execution"""
|
|
self.command_queue.put(cmd)
|
|
|
|
def stop(self):
|
|
"""Stop the worker thread"""
|
|
self.running = False
|