Technician tool v0.4

Imported from the Google Drive folder 004-technician_tool/v0.4.
This commit is contained in:
kkallas committed 2026-09-26 17:26:00 +02:00
1 parent e161941672
commit 72adfe1cec
19 files changed
+1818 -602

No files matched your search

+35 -1
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
from pathlib import Path
from PySide6.QtCore import QObject, Qt, QTimer, QUrl, Signal
from PySide6.QtCore import QCoreApplication, QObject, Qt, QTimer, QUrl, Signal
from PySide6.QtGui import QColor, QDesktopServices, QFont, QGuiApplication, QTextCharFormat, QTextCursor
from PySide6.QtWidgets import (
QCheckBox,
@@ -30,6 +30,7 @@ from ..serialio.manager import SerialManager, State
from ..serialio.ports import PortInfo, list_serial_ports
from .cheatsheet import cheatsheet_html
from .diag_panel import DiagnosticsPanel
from .flash_tab import FlashTab
from .guide_panel import GuidePanel
MAX_LINES = 20000
@@ -180,6 +181,9 @@ class SerialTab(QWidget):
port = self._selected_port()
if port is None:
return
self._connect(port)
def _connect(self, port: PortInfo) -> None:
self._counts = {"E": 0, "W": 0, "boots": 0}
self._engine.reset()
self._runner.cancel()
@@ -187,8 +191,36 @@ class SerialTab(QWidget):
self._session.start(port.mac or port.device)
self._manager.connect(port)
# ---- hand-off with the flash tab ------------------------------------
@property
def engine(self) -> DiagnosticsEngine:
return self._engine
def release_port(self) -> None:
"""Stop monitoring so esptool can open the port exclusively."""
if self._manager.is_running:
self._manager.disconnect() # joins the reader thread
QCoreApplication.processEvents() # deliver its queued final lines/state now
def connect_port(self, port: PortInfo, capture: bool = True) -> None:
"""Monitor `port`; with capture, restart the box once connected to record its boot."""
self.release_port()
idx = self.port_combo.findData(port.device)
if idx < 0:
self.port_combo.addItem(port.label, port.device)
self._ports.append(port)
idx = self.port_combo.count() - 1
self.port_combo.setCurrentIndex(idx)
self._reset_on_connect = capture
self._connect(port)
def _on_state(self, state: State, detail: str) -> None:
running = state != State.DISCONNECTED
if not running and self._manager.is_running:
return # stale event from a session that was already replaced
if state == State.CONNECTED and getattr(self, "_reset_on_connect", False):
self._reset_on_connect = False
QTimer.singleShot(300, self._manager.reset_board)
if not running and self._session.path is not None:
self._session.write_report(self._engine.report().to_text())
self._session.stop()
@@ -315,8 +347,10 @@ class MainWindow(QMainWindow):
guide = QTextBrowser(openExternalLinks=True)
guide.setHtml(cheatsheet_html())
self.serial_tab = SerialTab()
self.flash_tab = FlashTab(self.serial_tab)
tabs.addTab(guide, "Quick start")
tabs.addTab(self.serial_tab, "Serial monitor")
tabs.addTab(self.flash_tab, "Flash firmware")
self.setCentralWidget(tabs)
def closeEvent(self, event): # noqa: N802 (Qt naming)