#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
串口功能综合测试
测试当前连接的串口是否能用于升级、调试或业务通信
"""

import os
import sys
import termios
import fcntl
import select
import time

PORT = '/dev/cu.usbmodem21302'


def open_serial(port, baudrate):
    fd = os.open(port, os.O_RDWR | os.O_NOCTTY)
    old_attr = termios.tcgetattr(fd)
    new_attr = termios.tcgetattr(fd)
    new_attr[0] = 0
    new_attr[1] = 0
    new_attr[2] = termios.CS8 | termios.CREAD | termios.CLOCAL
    new_attr[3] = 0
    new_attr[6][termios.VMIN] = 0
    new_attr[6][termios.VTIME] = 1

    attr_name = f'B{baudrate}'
    if not hasattr(termios, attr_name):
        os.close(fd)
        return None, None
    baud_const = getattr(termios, attr_name)
    new_attr[4] = baud_const
    new_attr[5] = baud_const
    termios.tcsetattr(fd, termios.TCSANOW, new_attr)

    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl & ~os.O_NONBLOCK)
    return fd, old_attr


def close_serial(fd, old_attr):
    if fd is not None:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_attr)
        os.close(fd)


def read_all(fd, timeout_sec):
    data = b''
    start = time.time()
    while time.time() - start < timeout_sec:
        ready, _, _ = select.select([fd], [], [], 0.05)
        if ready:
            try:
                chunk = os.read(fd, 4096)
                if chunk:
                    data += chunk
            except OSError:
                break
        elif data:
            break
    return data


def test_baudrate(port, baudrate):
    """测试单个波特率"""
    fd, old_attr = open_serial(port, baudrate)
    if fd is None:
        print(f"  [{baudrate}] 系统不支持此波特率")
        return None

    try:
        # 清空已有数据
        read_all(fd, 0.5)

        # 发送 Esc 键（BootROM 检测）
        os.write(fd, b'\x1b')
        time.sleep(0.3)
        esc_data = read_all(fd, 1.0)

        # 发送换行（Shell 检测）
        os.write(fd, b'\n')
        time.sleep(0.3)
        nl_data = read_all(fd, 1.0)

        # 发送 help（Shell 检测）
        os.write(fd, b'help\n')
        time.sleep(0.3)
        help_data = read_all(fd, 1.0)

        all_data = esc_data + nl_data + help_data
        return all_data
    finally:
        close_serial(fd, old_attr)


def analyze_data(data):
    """分析数据特征"""
    if not data:
        return "无数据", []

    text = data.decode('utf-8', errors='replace')

    # 检测特征
    features = []
    keywords = {
        b'Realtek>': 'BootROM提示符(Realtek>)',
        b'd/g/r>': 'BootROM空片模式(d/g/r>)',
        b'Linux version': 'Linux启动日志',
        b'Starting kernel': '内核启动',
        b'FreeRTOS': 'FreeRTOS系统',
        b'U-Boot': 'U-Boot引导',
        b'# ': 'Shell提示符(#)',
        b'$ ': 'Shell提示符($)',
        b'> ': '命令提示符(>)',
        b'login:': '登录提示',
        b'password:': '密码提示',
        b'BusyBox': 'BusyBox环境',
        b'Android': 'Android系统',
        b'Build:': '构建信息',
        b'Project:': '项目信息',
    }

    for kw, desc in keywords.items():
        if kw.lower() in data.lower():
            features.append(desc)

    # 统计可打印 ASCII 比例
    printable = sum(1 for b in data if 32 <= b <= 126 or b in (10, 13))
    ratio = printable / len(data) if data else 0

    # 检测重复模式（可能是二进制协议）
    if len(data) >= 20:
        first_20 = data[:20]
        repeats = data.count(first_20)
        if repeats >= 3:
            features.append(f'高度重复模式({repeats}次)')

    readable = text[:200].replace('\n', ' ').replace('\r', ' ')
    readable = ' '.join(readable.split())

    return f"{len(data)}字节/可打印率{ratio:.1%}", features, readable[:150]


def passive_listen(port, baudrate, duration=5):
    """被动监听，不发送任何数据"""
    fd, old_attr = open_serial(port, baudrate)
    if fd is None:
        return None

    try:
        data = read_all(fd, duration)
        return data
    finally:
        close_serial(fd, old_attr)


def main():
    print("=" * 65)
    print("  串口功能综合测试")
    print("=" * 65)
    print(f"目标串口: {PORT}")
    print()

    # 第一步：被动监听（不发送数据，看是否有持续输出）
    print("【步骤1】被动监听 115200 bps（5秒），观察是否有自发数据...")
    passive_data = passive_listen(PORT, 115200, 5)
    if passive_data:
        info, features, readable = analyze_data(passive_data)
        print(f"  结果: {info}")
        print(f"  特征: {features if features else '无已知特征'}")
        print(f"  内容: {readable}")
    else:
        print("  结果: 5秒内无自发数据")
    print()

    # 第二步：多波特率主动探测
    baud_rates = [115200, 57600, 38400, 19200, 9600, 230400]
    print("【步骤2】多波特率主动探测（发送 Esc / 换行 / help）...")
    for baud in baud_rates:
        data = test_baudrate(PORT, baud)
        if data is None:
            continue
        if data:
            info, features, readable = analyze_data(data)
            print(f"  [{baud:>6}] 有响应 -> {info}")
            if features:
                print(f"          特征: {features}")
            print(f"          内容: {readable}")
        else:
            print(f"  [{baud:>6}] 无响应")
    print()

    # 第三步：结论
    print("=" * 65)
    print("【分析结论】")
    print()
    print("基于以上测试，判断此串口用途：")
    print()
    print("  1. 如果在 115200 下持续有大量自发数据，且为乱码/二进制：")
    print("     -> 这很可能是 MCU-ARM 业务通信口，不能用于固件升级")
    print()
    print("  2. 如果发送 Esc 后返回 'Realtek>' 或 'd/g/r>'：")
    print("     -> 这是 BootROM UART0，可以用于固件升级！")
    print("     -> 升级命令: 输入 'boot ru' 进入升级模式")
    print()
    print("  3. 如果返回 Linux 日志、Shell 提示符或命令响应：")
    print("     -> 这是 Linux 调试串口，可查看日志/调试系统")
    print("     -> 但不能直接烧录固件，需通过系统内 OTA 或切换到 UART0")
    print()
    print("=" * 65)


if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print("\n[!] 用户取消")
        sys.exit(0)
