import network
import socket
from machine import UART, Pin
import time
import gc
import json

# 全局变量
rs485_data = []
max_data_length = 1000
current_baudrate = 4800
display_format = "ascii"
uart_instance = None  # 全局UART实例

# 配置AP模式（开放网络）
def setup_ap():
    ap = network.WLAN(network.AP_IF)
    ap.active(True)
    # 创建开放网络，无需密码
    ap.config(essid='ESP32-RS485-Web', password='', authmode=0)
    ap.ifconfig(('192.168.4.1', '255.255.255.0', '192.168.4.1', '8.8.8.8'))
    
    while not ap.active():
        pass
    
    print('AP模式已启动 - 开放网络')
    print('网络名称: ESP32-RS485-Web')
    print('IP地址: 192.168.4.1')
    return ap

# 初始化RS485串口
def setup_rs485(baudrate=4800):
    global uart_instance
    # 如果已有实例，先关闭
    if uart_instance:
        try:
            uart_instance.deinit()
            time.sleep(0.1)
        except:
            pass
    
    # 创建新的UART实例
    uart_instance = UART(2, baudrate=baudrate, bits=8, parity=None, stop=1, rx=16, tx=17)
    print(f"RS485串口已初始化，波特率: {baudrate}")
    return uart_instance

# 读取RS485数据（增强稳定性）
def read_rs485_data(uart):
    global rs485_data
    try:
        if uart and uart.any():
            data = uart.read()
            if data:
                timestamp = time.ticks_ms()
                hex_data = data.hex()
                ascii_data = ''.join([chr(b) if 32 <= b < 127 else '.' for b in data])
                
                # 新数据插入到前面
                rs485_data.insert(0, {
                    'timestamp': timestamp,
                    'hex': hex_data,
                    'ascii': ascii_data,
                    'length': len(data)
                })
                
                # 限制数据列表长度
                if len(rs485_data) > max_data_length:
                    rs485_data = rs485_data[:max_data_length]
                
                print(f"收到RS485数据: {hex_data} (ASCII: {ascii_data})")
                return True
    except Exception as e:
        print(f"读取RS485数据错误: {e}")
        # 尝试重新初始化串口
        try:
            setup_rs485(current_baudrate)
        except:
            pass
    
    return False

# 发送数据到RS485
def send_rs485_data(uart, data_str, data_type='hex'):
    try:
        if not uart:
            return False, "串口未初始化"
            
        if data_type == 'hex':
            data_str = data_str.replace(' ', '').replace('\n', '').replace('\r', '')
            if len(data_str) % 2 != 0:
                return False, "十六进制数据长度必须为偶数"
            
            try:
                data_bytes = bytes.fromhex(data_str)
            except ValueError:
                return False, "无效的十六进制数据"
                
        elif data_type == 'ascii':
            data_bytes = data_str.encode('utf-8')
        else:
            return False, "未知的数据类型"
        
        uart.write(data_bytes)
        print(f"发送数据到RS485: {data_bytes.hex()}")
        return True, "数据发送成功"
        
    except Exception as e:
        return False, f"发送失败: {str(e)}"

# 生成数据表格HTML
def generate_data_table():
    global rs485_data, display_format
    
    data_rows = ""
    if not rs485_data:
        data_rows = "<tr><td colspan='4' style='text-align: center;'>暂无数据</td></tr>"
    else:
        for i, data in enumerate(rs485_data[:50]):
            time_str = f"{time.ticks_ms() - data['timestamp']}ms前"
            data_content = data['hex'] if display_format == "hex" else data['ascii']
            
            data_rows += f"""
            <tr>
                <td>{i+1}</td>
                <td>{time_str}</td>
                <td>{data_content}</td>
                <td>{data['length']}</td>
            </tr>
            """
    
    return data_rows

# 生成网页HTML
def generate_html(status_message=""):
    global rs485_data, current_baudrate, display_format
    
    html = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <title>ESP32 RS485监控</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <style>
            body {{ font-family: Arial, sans-serif; margin: 20px; background-color: #f5f5f5; }}
            .container {{ max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }}
            h1 {{ color: #333; text-align: center; }}
            .section {{ margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }}
            table {{ width: 100%; border-collapse: collapse; margin: 10px 0; }}
            th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
            th {{ background-color: #f2f2f2; }}
            .form-group {{ margin: 10px 0; }}
            label {{ display: inline-block; width: 150px; }}
            input, select, textarea {{ padding: 5px; margin: 5px 0; }}
            button {{ background-color: #4CAF50; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; margin: 5px; }}
            button:hover {{ background-color: #45a049; }}
            .btn-danger {{ background-color: #f44336; }}
            .btn-danger:hover {{ background-color: #da190b; }}
            .status {{ padding: 10px; margin: 10px 0; border-radius: 4px; }}
            .success {{ background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }}
            .error {{ background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }}
            #data-table {{ max-height: 400px; overflow-y: auto; display: block; }}
            .new-data {{ background-color: #e8f5e8; transition: background-color 2s; }}
            .display-selector {{ margin-left: 20px; }}
            .connection-status {{ display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 5px; }}
            .connected {{ background-color: #4CAF50; }}
            .disconnected {{ background-color: #f44336; }}
        </style>
        <script>
            var currentDisplayFormat = '{display_format}';
            var isConnected = true;
            
            // 刷新数据表格
            function refreshData() {{
                var xhr = new XMLHttpRequest();
                xhr.onreadystatechange = function() {{
                    if (xhr.readyState === 4) {{
                        if (xhr.status === 200) {{
                            document.getElementById('data-body').innerHTML = xhr.responseText;
                            document.getElementById('data-count').innerText = document.getElementById('data-body').rows.length;
                            
                            // 高亮新数据
                            var rows = document.getElementById('data-body').getElementsByTagName('tr');
                            if (rows.length > 0) {{
                                rows[0].classList.add('new-data');
                                setTimeout(function() {{
                                    if (rows.length > 0) {{
                                        rows[0].classList.remove('new-data');
                                    }}
                                }}, 2000);
                            }}
                            updateConnectionStatus(true);
                        }} else {{
                            updateConnectionStatus(false);
                        }}
                    }}
                }};
                xhr.open('GET', '/data?format=' + currentDisplayFormat, true);
                xhr.timeout = 5000;
                xhr.ontimeout = function() {{
                    updateConnectionStatus(false);
                }};
                xhr.send();
            }}
            
            // 更新连接状态显示
            function updateConnectionStatus(connected) {{
                isConnected = connected;
                var statusElement = document.getElementById('connection-status');
                var statusText = document.getElementById('connection-status-text');
                
                if (connected) {{
                    statusElement.className = 'connection-status connected';
                    statusText.innerText = '已连接';
                    statusText.style.color = '#4CAF50';
                }} else {{
                    statusElement.className = 'connection-status disconnected';
                    statusText.innerText = '连接中断';
                    statusText.style.color = '#f44336';
                }}
            }}
            
            // 切换显示格式
            function switchDisplayFormat() {{
                var formatSelect = document.getElementById('display-format');
                currentDisplayFormat = formatSelect.value;
                
                var header = document.getElementById('data-content-header');
                header.innerText = currentDisplayFormat === 'hex' ? '数据内容 (十六进制)' : '数据内容 (ASCII)';
                
                refreshData();
            }}
            
            // 修改波特率
            function updateBaudrate() {{
                var baudrate = document.getElementById('baudrate').value;
                var xhr = new XMLHttpRequest();
                xhr.open('POST', '/baudrate', true);
                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                xhr.onload = function() {{
                    if (xhr.status === 200) {{
                        var response = JSON.parse(xhr.responseText);
                        if (response.success) {{
                            showStatus('波特率已修改为: ' + baudrate, 'success');
                            document.getElementById('current-baudrate').innerText = baudrate;
                            // 不刷新页面，继续正常数据更新
                            refreshData();
                        }} else {{
                            showStatus('波特率修改失败: ' + response.message, 'error');
                        }}
                    }} else {{
                        showStatus('网络错误，请检查连接', 'error');
                    }}
                }};
                xhr.onerror = function() {{
                    showStatus('网络错误，请检查连接', 'error');
                }};
                xhr.send('baudrate=' + baudrate);
                return false;
            }}
            
            // 显示状态消息
            function showStatus(message, type) {{
                var statusDiv = document.getElementById('status-message');
                statusDiv.innerHTML = '<div class="status ' + type + '">' + message + '</div>';
                setTimeout(function() {{
                    statusDiv.innerHTML = '';
                }}, 3000);
            }}
            
            // 设置定时器
            setInterval(refreshData, 1000);
            window.onload = refreshData;
        </script>
    </head>
    <body>
        <div class="container">
            <h1>ESP32 RS485监控界面</h1>
            <div id="status-message"></div>
            
            <!-- 接收数据 -->
            <div class="section">
                <h3>接收数据 (最近50条，新数据在前) 
                    <select id="display-format" class="display-selector" onchange="switchDisplayFormat()">
                        <option value="ascii" {'selected' if display_format == 'ascii' else ''}>ASCII</option>
                        <option value="hex" {'selected' if display_format == 'hex' else ''}>十六进制</option>
                    </select>
                    <span style="margin-left: 20px;">
                        <span id="connection-status" class="connection-status connected"></span>
                        <span id="connection-status-text" style="color: #4CAF50;">已连接</span>
                    </span>
                </h3>
                <div id="data-table">
                    <table>
                        <thead>
                            <tr>
                                <th>序号</th>
                                <th>时间</th>
                                <th id="data-content-header">数据内容 (ASCII)</th>
                                <th>长度</th>
                            </tr>
                        </thead>
                        <tbody id="data-body">
                            {generate_data_table()}
                        </tbody>
                    </table>
                </div>
            </div>
            
            <!-- 系统状态 -->
            <div class="section">
                <h3>系统状态</h3>
                <p><strong>IP地址:</strong> 192.168.4.1</p>
                <p><strong>当前波特率:</strong> <span id="current-baudrate">{current_baudrate}</span> bps</p>
                <p><strong>数据条数:</strong> <span id="data-count">{len(rs485_data)}</span></p>
            </div>
            
            <!-- 串口配置 -->
            <div class="section">
                <h3>串口配置</h3>
                <form onsubmit="return updateBaudrate()">
                    <div class="form-group">
                        <label for="baudrate">波特率:</label>
                        <select id="baudrate" name="baudrate">
                            <option value="4800" {'selected' if current_baudrate == 4800 else ''}>4800</option>
                            <option value="9600" {'selected' if current_baudrate == 9600 else ''}>9600</option>
                            <option value="19200" {'selected' if current_baudrate == 19200 else ''}>19200</option>
                            <option value="38400" {'selected' if current_baudrate == 38400 else ''}>38400</option>
                            <option value="57600" {'selected' if current_baudrate == 57600 else ''}>57600</option>
                            <option value="115200" {'selected' if current_baudrate == 115200 else ''}>115200</option>
                        </select>
                        <button type="submit">应用波特率</button>
                    </div>
                </form>
            </div>
            
            <!-- 数据控制 -->
            <div class="section">
                <h3>数据控制</h3>
                <button type="button" class="btn-danger" onclick="clearData()">清除所有数据</button>
            </div>
            
            <!-- 发送数据 -->
            <div class="section">
                <h3>发送数据</h3>
                <form method="post" action="/send" onsubmit="return sendData()">
                    <div class="form-group">
                        <label for="data_type">数据类型:</label>
                        <select id="data_type" name="data_type">
                            <option value="hex">十六进制</option>
                            <option value="ascii">ASCII文本</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label for="send_data">发送数据:</label><br>
                        <textarea id="send_data" name="send_data" rows="3" cols="50" placeholder="输入要发送的数据..."></textarea>
                    </div>
                    <div class="form-group">
                        <button type="submit">发送数据</button>
                    </div>
                </form>
            </div>
        </div>
    </body>
    </html>
    """
    return html

# 处理HTTP请求
def handle_http_request(client_socket, uart):
    global rs485_data, current_baudrate, display_format
    
    try:
        request = client_socket.recv(1024).decode('utf-8')
        print("收到HTTP请求")
        
        request_line = request.split('\r\n')[0]
        method, path, _ = request_line.split(' ')
        
        response = ""
        content_type = "text/html"
        status_message = ""
        
        if method == 'GET' and path == '/':
            response = generate_html()
            
        elif method == 'GET' and path.startswith('/data'):
            if 'format=' in path:
                format_param = path.split('format=')[1].split('&')[0]
                display_format = format_param if format_param in ['hex', 'ascii'] else 'ascii'
            
            response = generate_data_table()
            content_type = "text/html"
            
        elif method == 'POST':
            if path == '/baudrate':
                content_length = 0
                for line in request.split('\r\n'):
                    if line.startswith('Content-Length:'):
                        content_length = int(line.split(':')[1].strip())
                        break
                
                if content_length > 0:
                    post_data = request.split('\r\n\r\n')[-1][:content_length]
                    params = {}
                    for pair in post_data.split('&'):
                        key, value = pair.split('=')
                        params[key] = value
                    
                    new_baudrate = int(params.get('baudrate', '4800'))
                    try:
                        setup_rs485(new_baudrate)
                        current_baudrate = new_baudrate
                        print(f"波特率已修改为: {new_baudrate}")
                        # 返回JSON响应而不是重定向
                        response = json.dumps({"success": True, "message": "波特率修改成功"})
                        content_type = "application/json"
                    except Exception as e:
                        response = json.dumps({"success": False, "message": str(e)})
                        content_type = "application/json"
                
            elif path == '/send':
                content_length = 0
                for line in request.split('\r\n'):
                    if line.startswith('Content-Length:'):
                        content_length = int(line.split(':')[1].strip())
                        break
                
                if content_length > 0:
                    post_data = request.split('\r\n\r\n')[-1][:content_length]
                    params = {}
                    for pair in post_data.split('&'):
                        key, value = pair.split('=')
                        params[key] = value
                    
                    data_type = params.get('data_type', 'hex')
                    send_data = params.get('send_data', '')
                    
                    success, message = send_rs485_data(uart, send_data, data_type)
                    if success:
                        status_message = f"<div class='status success'>{message}</div>"
                    else:
                        status_message = f"<div class='status error'>{message}</div>"
                
            elif path == '/clear':
                rs485_data = []
                print("数据已清除")
                status_message = "<div class='status success'>数据已清除</div>"
            
            if content_type == "text/html":
                response = generate_html(status_message)
        
        # 发送HTTP响应
        http_response = f"""HTTP/1.1 200 OK
Content-Type: {content_type}; charset=utf-8
Connection: close

{response}"""
        
        client_socket.send(http_response.encode('utf-8'))
        
    except Exception as e:
        print("HTTP处理错误:", e)
    finally:
        client_socket.close()

# 主函数
def main():
    global current_baudrate, uart_instance
    
    # 设置AP模式（开放网络）
    ap = setup_ap()
    
    # 初始化RS485
    uart_instance = setup_rs485(current_baudrate)
    
    # 创建TCP服务器
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(('0.0.0.0', 80))
    server_socket.listen(5)
    server_socket.settimeout(0.5)
    
    print("Web服务器已启动，端口: 80")
    print("打开浏览器访问: http://192.168.4.1")
    
    last_memory_clean = time.ticks_ms()
    
    while True:
        try:
            # 检查RS485数据
            if uart_instance:
                read_rs485_data(uart_instance)
            
            # 检查HTTP连接
            try:
                client_socket, addr = server_socket.accept()
                print("客户端连接来自:", addr)
                handle_http_request(client_socket, uart_instance)
            except socket.timeout:
                pass
            except OSError as e:
                if e.args[0] != 110:  # 忽略超时错误
                    raise
            
            # 定期清理内存
            if time.ticks_ms() - last_memory_clean > 30000:
                gc.collect()
                last_memory_clean = time.ticks_ms()
                
        except Exception as e:
            print("主循环错误:", e)
            time.sleep(1)

if __name__ == "__main__":
    main()