feat(uart): add UART availability check, inter-step delays, and post-download Zynq status verification

- Add check_uart_available() to serial_monitor.py — detects port availability
- Add verify_zynq_status() to boot_verifier.py — lightweight post-download check
- Add uart_delay / inter_step_delay config fields (defaults: 3s / 2s)
- Step 1: Check UART availability, show non-blocking 'UART 不可用' notification
- Step 4: Add post-download Zynq status check (ping + serial) after TFTP
- _run_steps_worker: Add configurable inter-step delays between all steps
- Update step labels (4a→4b→4c→4d) for clarity
This commit is contained in:
Jeremy Shen
2026-06-09 17:53:48 +08:00
parent 91a7843dbe
commit 3a9bc5d9ed
4 changed files with 209 additions and 7 deletions
+94
View File
@@ -124,6 +124,100 @@ def verify_boot(
)
def verify_zynq_status(
config: Config,
timeout: int | None = None,
max_retries: int = 3,
uart_available: bool = True,
callback: ProgressCallback = None,
) -> BootVerificationResult:
"""Quickly verify Zynq is responding after a download operation.
Performs a fast ping check and optionally a serial port read.
Unlike verify_boot, this is a lightweight check meant to confirm
the device is alive after flash/TFTP operations (before reboot).
Args:
config: Application configuration.
timeout: Seconds per ping attempt.
max_retries: Number of ping retry attempts.
uart_available: Whether UART serial port is available.
callback: Optional progress callback.
Returns:
BootVerificationResult (ip_reachable and serial_found reflect
the quick check results).
"""
timeout = timeout or config.ping_timeout
ip = config.zynq_ip
if callback:
callback("start", f"Checking Zynq status on {ip}...")
# Check 1: Ping
ip_reachable = False
for attempt in range(1, max_retries + 1):
if callback:
callback("progress", f"Ping attempt {attempt}/{max_retries}...")
if ping_ip(ip, 1, timeout):
ip_reachable = True
if callback:
callback("complete", f"Device {ip} is reachable")
break
time.sleep(1)
# Check 2: Serial (only if UART is available)
serial_found = False
boot_info: dict = {}
if uart_available and config.serial_port:
ports = detect_serial_ports()
if ports:
for port_info in ports:
if port_info.device == config.serial_port:
try:
lines = read_serial_stream(
config.serial_port,
config.serial_baudrate,
timeout=3,
)
parsed = parse_boot_output(lines)
if parsed.ip_address or parsed.boot_message or parsed.version:
serial_found = True
boot_info = {
"ip_address": parsed.ip_address,
"version": parsed.version,
"boot_message": parsed.boot_message,
"raw_lines": lines[-20:],
}
if callback:
callback("complete", "Boot messages detected on serial")
break
except Exception:
pass # Non-fatal; fall through
success = ip_reachable or serial_found
message_parts = []
if ip_reachable:
message_parts.append(f"IP {ip} reachable")
if serial_found:
message_parts.append("serial boot info found")
if not success:
message_parts.append("Zynq not responding")
message = "; ".join(message_parts) if message_parts else "Check inconclusive"
if callback:
callback("complete" if success else "error", message)
return BootVerificationResult(
success=success,
message=message,
ip_reachable=ip_reachable,
serial_found=serial_found,
boot_info=boot_info if boot_info else {},
)
def wait_for_boot(
config: Config,
timeout: int | None = None,