|
|
@@ -58,8 +58,8 @@ class DeviceController(QObject):
|
|
|
|
self._data_dir = data_dir
|
|
|
|
self._data_dir = data_dir
|
|
|
|
self._service = DeviceService(settings)
|
|
|
|
self._service = DeviceService(settings)
|
|
|
|
|
|
|
|
|
|
|
|
# Initialize status from persisted settings (with defaults for new installations)
|
|
|
|
# Always start fresh — never restore connection status from a prior session
|
|
|
|
self._connection_status = getattr(settings, 'connection_status', "Disconnected")
|
|
|
|
self._connection_status = "Disconnected"
|
|
|
|
self._operation_in_progress = False
|
|
|
|
self._operation_in_progress = False
|
|
|
|
self._activity_log: list[str] = getattr(settings, 'activity_log', [])
|
|
|
|
self._activity_log: list[str] = getattr(settings, 'activity_log', [])
|
|
|
|
self._raw_bytes: Optional[bytes] = None
|
|
|
|
self._raw_bytes: Optional[bytes] = None
|
|
|
@@ -87,6 +87,9 @@ class DeviceController(QObject):
|
|
|
|
self._append_log(f"Last wafer: {info.get('serialNumber', 'Unknown')} ({info.get('familyCode', 'Unknown')})")
|
|
|
|
self._append_log(f"Last wafer: {info.get('serialNumber', 'Unknown')} ({info.get('familyCode', 'Unknown')})")
|
|
|
|
self.statusRestored.emit()
|
|
|
|
self.statusRestored.emit()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Reset connection status on fresh start
|
|
|
|
|
|
|
|
self._connection_status = "Disconnected"
|
|
|
|
|
|
|
|
|
|
|
|
# Marshal worker-thread results onto the Qt main thread.
|
|
|
|
# Marshal worker-thread results onto the Qt main thread.
|
|
|
|
self._detectFinished.connect(self._handle_detect_finished, Qt.ConnectionType.QueuedConnection)
|
|
|
|
self._detectFinished.connect(self._handle_detect_finished, Qt.ConnectionType.QueuedConnection)
|
|
|
|
self._readFinished.connect(self._handle_read_finished, Qt.ConnectionType.QueuedConnection)
|
|
|
|
self._readFinished.connect(self._handle_read_finished, Qt.ConnectionType.QueuedConnection)
|
|
|
@@ -101,6 +104,12 @@ class DeviceController(QObject):
|
|
|
|
"""Currently available serial port names."""
|
|
|
|
"""Currently available serial port names."""
|
|
|
|
return self._service.enumerate_ports()
|
|
|
|
return self._service.enumerate_ports()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _set_connection_status(self, status: str) -> None:
|
|
|
|
|
|
|
|
"""Update connection status and notify QML."""
|
|
|
|
|
|
|
|
if self._connection_status != status:
|
|
|
|
|
|
|
|
self._connection_status = status
|
|
|
|
|
|
|
|
self.portsUpdated.emit(self.availablePorts)
|
|
|
|
|
|
|
|
|
|
|
|
@Property(str, notify=portsUpdated)
|
|
|
|
@Property(str, notify=portsUpdated)
|
|
|
|
def connectionStatus(self) -> str:
|
|
|
|
def connectionStatus(self) -> str:
|
|
|
|
"""Current connection status string for display."""
|
|
|
|
"""Current connection status string for display."""
|
|
|
@@ -223,7 +232,7 @@ class DeviceController(QObject):
|
|
|
|
@slot_error_boundary
|
|
|
|
@slot_error_boundary
|
|
|
|
def clearSession(self) -> None:
|
|
|
|
def clearSession(self) -> None:
|
|
|
|
"""Clear the current session state, allowing a clean detect."""
|
|
|
|
"""Clear the current session state, allowing a clean detect."""
|
|
|
|
self._connection_status = "Disconnected"
|
|
|
|
self._set_connection_status("Disconnected")
|
|
|
|
self._selected_port = ""
|
|
|
|
self._selected_port = ""
|
|
|
|
self._last_wafer_info = {}
|
|
|
|
self._last_wafer_info = {}
|
|
|
|
self._data_row_count = 0
|
|
|
|
self._data_row_count = 0
|
|
|
@@ -235,6 +244,7 @@ class DeviceController(QObject):
|
|
|
|
self._append_log("Session refreshed/cleared")
|
|
|
|
self._append_log("Session refreshed/cleared")
|
|
|
|
self._save_status()
|
|
|
|
self._save_status()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _set_operation_progress(self, in_progress: bool) -> None:
|
|
|
|
def _set_operation_progress(self, in_progress: bool) -> None:
|
|
|
|
"""Set operation progress state and notify QML.
|
|
|
|
"""Set operation progress state and notify QML.
|
|
|
|
|
|
|
|
|
|
|
@@ -252,7 +262,7 @@ class DeviceController(QObject):
|
|
|
|
def refreshPorts(self) -> None:
|
|
|
|
def refreshPorts(self) -> None:
|
|
|
|
"""Scan for available serial ports and emit updated list."""
|
|
|
|
"""Scan for available serial ports and emit updated list."""
|
|
|
|
ports = self._service.enumerate_ports()
|
|
|
|
ports = self._service.enumerate_ports()
|
|
|
|
self._connection_status = "Disconnected"
|
|
|
|
self._set_connection_status("Disconnected")
|
|
|
|
self.portsUpdated.emit(ports)
|
|
|
|
self.portsUpdated.emit(ports)
|
|
|
|
self._append_log(f"Scanned {len(ports)} serial port(s)")
|
|
|
|
self._append_log(f"Scanned {len(ports)} serial port(s)")
|
|
|
|
self._save_status()
|
|
|
|
self._save_status()
|
|
|
@@ -271,7 +281,7 @@ class DeviceController(QObject):
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
self._set_operation_progress(True)
|
|
|
|
self._set_operation_progress(True)
|
|
|
|
self._connection_status = "Detecting..."
|
|
|
|
self._set_connection_status("Detecting...")
|
|
|
|
self.portsUpdated.emit(self._service.enumerate_ports())
|
|
|
|
self.portsUpdated.emit(self._service.enumerate_ports())
|
|
|
|
self._append_log("Scanning all ports for wafer ...")
|
|
|
|
self._append_log("Scanning all ports for wafer ...")
|
|
|
|
|
|
|
|
|
|
|
@@ -295,7 +305,7 @@ class DeviceController(QObject):
|
|
|
|
"""Main-thread completion handler for detectWafer."""
|
|
|
|
"""Main-thread completion handler for detectWafer."""
|
|
|
|
if info is not None:
|
|
|
|
if info is not None:
|
|
|
|
self._selected_port = port or ""
|
|
|
|
self._selected_port = port or ""
|
|
|
|
self._connection_status = "Connected"
|
|
|
|
self._set_connection_status("Connected")
|
|
|
|
self._append_log(
|
|
|
|
self._append_log(
|
|
|
|
f"Detected: {info.serial_number} (family={info.family_code}, "
|
|
|
|
f"Detected: {info.serial_number} (family={info.family_code}, "
|
|
|
|
f"port={port}, runtime={info.runtime}s, cycles={info.cycle_count})"
|
|
|
|
f"port={port}, runtime={info.runtime}s, cycles={info.cycle_count})"
|
|
|
@@ -306,7 +316,7 @@ class DeviceController(QObject):
|
|
|
|
self._save_status()
|
|
|
|
self._save_status()
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
self._selected_port = ""
|
|
|
|
self._selected_port = ""
|
|
|
|
self._connection_status = "Disconnected"
|
|
|
|
self._set_connection_status("Disconnected")
|
|
|
|
self._append_log("No wafer detected on any port")
|
|
|
|
self._append_log("No wafer detected on any port")
|
|
|
|
self._last_wafer_info = {}
|
|
|
|
self._last_wafer_info = {}
|
|
|
|
self.detectResult.emit(None)
|
|
|
|
self.detectResult.emit(None)
|
|
|
@@ -334,7 +344,7 @@ class DeviceController(QObject):
|
|
|
|
family_code = self._last_wafer_info.get("familyCode", "")
|
|
|
|
family_code = self._last_wafer_info.get("familyCode", "")
|
|
|
|
|
|
|
|
|
|
|
|
self._set_operation_progress(True)
|
|
|
|
self._set_operation_progress(True)
|
|
|
|
self._connection_status = "Reading..."
|
|
|
|
self._set_connection_status("Reading...")
|
|
|
|
self.portsUpdated.emit(self._service.enumerate_ports())
|
|
|
|
self.portsUpdated.emit(self._service.enumerate_ports())
|
|
|
|
self._append_log(
|
|
|
|
self._append_log(
|
|
|
|
f"Reading memory on {port} (family={family_code or 'auto'}) ..."
|
|
|
|
f"Reading memory on {port} (family={family_code or 'auto'}) ..."
|
|
|
@@ -362,7 +372,7 @@ class DeviceController(QObject):
|
|
|
|
) -> None:
|
|
|
|
) -> None:
|
|
|
|
"""Main-thread completion handler for readMemoryAsync."""
|
|
|
|
"""Main-thread completion handler for readMemoryAsync."""
|
|
|
|
if data is not None:
|
|
|
|
if data is not None:
|
|
|
|
self._connection_status = "Connected"
|
|
|
|
self._set_connection_status("Connected")
|
|
|
|
self._append_log(f"Read {len(data)} bytes")
|
|
|
|
self._append_log(f"Read {len(data)} bytes")
|
|
|
|
self._raw_bytes = data
|
|
|
|
self._raw_bytes = data
|
|
|
|
self.readResult.emit({"success": True, "bytes": len(data)})
|
|
|
|
self.readResult.emit({"success": True, "bytes": len(data)})
|
|
|
@@ -370,7 +380,7 @@ class DeviceController(QObject):
|
|
|
|
self._append_log("Memory read complete - choose save directory to save CSV")
|
|
|
|
self._append_log("Memory read complete - choose save directory to save CSV")
|
|
|
|
self._save_status()
|
|
|
|
self._save_status()
|
|
|
|
return
|
|
|
|
return
|
|
|
|
self._connection_status = "Disconnected"
|
|
|
|
self._set_connection_status("Disconnected")
|
|
|
|
self._append_log("Read returned no data")
|
|
|
|
self._append_log("Read returned no data")
|
|
|
|
self._raw_bytes = None
|
|
|
|
self._raw_bytes = None
|
|
|
|
self.readResult.emit({"error": "Read returned no data"})
|
|
|
|
self.readResult.emit({"error": "Read returned no data"})
|
|
|
@@ -382,16 +392,16 @@ class DeviceController(QObject):
|
|
|
|
def eraseMemory(self, port: str) -> None:
|
|
|
|
def eraseMemory(self, port: str) -> None:
|
|
|
|
"""Send p1 erase command."""
|
|
|
|
"""Send p1 erase command."""
|
|
|
|
self._set_operation_progress(True)
|
|
|
|
self._set_operation_progress(True)
|
|
|
|
self._connection_status = "Erasing..."
|
|
|
|
self._set_connection_status("Erasing...")
|
|
|
|
self.portsUpdated.emit(self._service.enumerate_ports())
|
|
|
|
self.portsUpdated.emit(self._service.enumerate_ports())
|
|
|
|
self._append_log(f"Erase command sent to {port} (wafer takes ~15s) ...")
|
|
|
|
self._append_log(f"Erase command sent to {port} (wafer takes ~15s) ...")
|
|
|
|
|
|
|
|
|
|
|
|
ok = self._service.erase_wafer(port)
|
|
|
|
ok = self._service.erase_wafer(port)
|
|
|
|
if ok:
|
|
|
|
if ok:
|
|
|
|
self._connection_status = "Connected"
|
|
|
|
self._set_connection_status("Connected")
|
|
|
|
self._append_log("Erase command accepted — wait ~15 seconds")
|
|
|
|
self._append_log("Erase command accepted — wait ~15 seconds")
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
self._connection_status = "Disconnected"
|
|
|
|
self._set_connection_status("Disconnected")
|
|
|
|
self._append_log("Erase command failed")
|
|
|
|
self._append_log("Erase command failed")
|
|
|
|
self.eraseResult.emit({"success": ok})
|
|
|
|
self.eraseResult.emit({"success": ok})
|
|
|
|
self._set_operation_progress(False)
|
|
|
|
self._set_operation_progress(False)
|
|
|
@@ -404,14 +414,14 @@ class DeviceController(QObject):
|
|
|
|
Emits debugResult with counts on success.
|
|
|
|
Emits debugResult with counts on success.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
self._set_operation_progress(True)
|
|
|
|
self._set_operation_progress(True)
|
|
|
|
self._connection_status = "Reading debug..."
|
|
|
|
self._set_connection_status("Reading debug...")
|
|
|
|
self.portsUpdated.emit(self._service.enumerate_ports())
|
|
|
|
self.portsUpdated.emit(self._service.enumerate_ports())
|
|
|
|
self._append_log(f"Reading debug data on {port} ...")
|
|
|
|
self._append_log(f"Reading debug data on {port} ...")
|
|
|
|
|
|
|
|
|
|
|
|
# Step 1: D1 sensor data
|
|
|
|
# Step 1: D1 sensor data
|
|
|
|
sensor_data = self._service.read_wafer_data(port, family_code="")
|
|
|
|
sensor_data = self._service.read_wafer_data(port, family_code="")
|
|
|
|
if sensor_data is None:
|
|
|
|
if sensor_data is None:
|
|
|
|
self._connection_status = "Disconnected"
|
|
|
|
self._set_connection_status("Disconnected")
|
|
|
|
self._append_log("D1 read failed — aborting debug read")
|
|
|
|
self._append_log("D1 read failed — aborting debug read")
|
|
|
|
self.debugResult.emit({"error": "D1 read failed"})
|
|
|
|
self.debugResult.emit({"error": "D1 read failed"})
|
|
|
|
self._set_operation_progress(False)
|
|
|
|
self._set_operation_progress(False)
|
|
|
@@ -422,13 +432,13 @@ class DeviceController(QObject):
|
|
|
|
# Step 2: F1 debug data
|
|
|
|
# Step 2: F1 debug data
|
|
|
|
debug_data = self._service.read_wafer_data(port, family_code="", cmd="F1")
|
|
|
|
debug_data = self._service.read_wafer_data(port, family_code="", cmd="F1")
|
|
|
|
if debug_data is None:
|
|
|
|
if debug_data is None:
|
|
|
|
self._connection_status = "Disconnected"
|
|
|
|
self._set_connection_status("Disconnected")
|
|
|
|
self._append_log("F1 read failed")
|
|
|
|
self._append_log("F1 read failed")
|
|
|
|
self.debugResult.emit({"error": "F1 read failed"})
|
|
|
|
self.debugResult.emit({"error": "F1 read failed"})
|
|
|
|
self._set_operation_progress(False)
|
|
|
|
self._set_operation_progress(False)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
self._connection_status = "Connected"
|
|
|
|
self._set_connection_status("Connected")
|
|
|
|
self._append_log(
|
|
|
|
self._append_log(
|
|
|
|
f"Debug read complete: {len(sensor_data)} sensor bytes, "
|
|
|
|
f"Debug read complete: {len(sensor_data)} sensor bytes, "
|
|
|
|
f"{len(debug_data)} debug bytes"
|
|
|
|
f"{len(debug_data)} debug bytes"
|
|
|
|