html+js开发一个测试工具

index.html

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>接口调用测试工具</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }
        
        .container {
            width: 100%;
            max-width: 1200px;
            background-color: white;
            border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            overflow: hidden;
        }
        
        header {
            background: linear-gradient(90deg, #4b6cb7 0%, #182848 100%);
            color: white;
            padding: 20px;
            text-align: center;
        }
        
        h1 {
            font-size: 1.8rem;
            margin-bottom: 10px;
        }
        
        .subtitle {
            font-size: 1rem;
            opacity: 0.8;
        }
        
        .content {
            padding: 25px;
        }
        
        .connection-section {
            display: flex;
            gap: 15px;
            margin-bottom: 20px;
            flex-wrap: wrap;
            align-items: center;
        }
        
        .connection-input {
            flex: 1;
            min-width: 150px;
            padding: 10px 15px;
            border: 1px solid #ced4da;
            border-radius: 6px;
            font-size: 0.9rem;
        }
        
        .connection-input:focus {
            outline: none;
            border-color: #4b6cb7;
            box-shadow: 0 0 0 2px rgba(75, 108, 183, 0.2);
        }
        
        .connection-btn {
            padding: 10px 20px;
            background-color: #28a745;
            color: white;
            border: none;
            border-radius: 6px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        
        .connection-btn:hover {
            background-color: #218838;
        }
        
        .connection-btn.disconnected {
            background-color: #dc3545;
        }
        
        .connection-btn.disconnected:hover {
            background-color: #c82333;
        }
        
        .connection-status {
            display: flex;
            align-items: center;
            gap: 8px;
            padding: 10px 15px;
            border-radius: 6px;
            font-size: 0.9rem;
            font-weight: 500;
        }
        
        .status-connected {
            background-color: #d4edda;
            color: #155724;
        }
        
        .status-disconnected {
            background-color: #f8d7da;
            color: #721c24;
        }
        
        .status-indicator {
            width: 10px;
            height: 10px;
            border-radius: 50%;
        }
        
        .status-connected .status-indicator {
            background-color: #28a745;
        }
        
        .status-disconnected .status-indicator {
            background-color: #dc3545;
        }
        
        .function-section {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            align-items: center;
            flex-wrap: wrap;
        }
        
        .function-input {
            flex: 1;
            min-width: 200px;
            padding: 10px 15px;
            border: 1px solid #ced4da;
            border-radius: 6px;
            font-size: 0.9rem;
        }
        
        .function-input:focus {
            outline: none;
            border-color: #4b6cb7;
            box-shadow: 0 0 0 2px rgba(75, 108, 183, 0.2);
        }
        
        .function-label {
            font-weight: 500;
            color: #495057;
            min-width: 80px;
        }
        
        .query-section {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            align-items: center;
        }
        
        .query-input {
            flex: 1;
            padding: 10px 15px;
            border: 1px solid #ced4da;
            border-radius: 6px;
            font-size: 0.9rem;
            max-width: 300px;
        }
        
        .query-input:focus {
            outline: none;
            border-color: #4b6cb7;
            box-shadow: 0 0 0 2px rgba(75, 108, 183, 0.2);
        }
        
        .query-btn {
            padding: 10px 20px;
            background-color: #17a2b8;
            color: white;
            border: none;
            border-radius: 6px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        
        .query-btn:hover {
            background-color: #138496;
        }
        
        .table-container {
            width: 100%;
            overflow-x: auto;
            margin-bottom: 30px;
        }
        
        .variable-table {
            width: 100%;
            border-collapse: collapse;
            font-size: 0.9rem;
            background-color: white;
            border-radius: 8px;
            overflow: hidden;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
            line-height: 1.2;
        }
        
        .variable-table th {
            background-color: #4b6cb7;
            color: white;
            padding: 8px 20px;
            text-align: left;
            font-weight: 600;
            font-size: 1rem;
            border: none;
        }
        
        .variable-table th:first-child {
            border-top-left-radius: 8px;
        }
        
        .variable-table th:last-child {
            border-top-right-radius: 8px;
        }
        
        .variable-table td {
            padding: 6px 20px;
            border-bottom: 1px solid #e9ecef;
            vertical-align: middle;
        }
        
        .variable-table tr:last-child td {
            border-bottom: none;
        }
        
        .variable-table tr:nth-child(even) {
            background-color: #f8f9fa;
        }
        
        .variable-table tr:hover {
            background-color: #e3f2fd;
        }
        
        .variable-table tr.highlighted {
            background-color: #fff3cd !important;
            border: 2px solid #ffc107;
        }
        
        .variable-table tr.highlighted td {
            font-weight: bold;
            color: #856404;
        }
        
        .index-column {
            width: 8%;
            text-align: center;
            font-weight: 600;
            color: #495057;
        }
        
        .name-column {
            width: 32%;
            font-family: 'Courier New', monospace;
            color: #2c4a8f;
            font-weight: 600;
        }
        
        .type-column {
            width: 25%;
            font-family: 'Courier New', monospace;
            color: #495057;
        }
        
        .value-column {
            width: 35%;
        }
        
        .toggle-btn {
            background: none;
            border: none;
            cursor: pointer;
            font-size: 1rem;
            width: 20px;
            height: 20px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            margin-right: 8px;
            border-radius: 3px;
            transition: all 0.2s ease;
        }
        
        .toggle-btn:hover {
            background-color: rgba(0, 0, 0, 0.1);
        }
        
        .struct-row {
            background-color: #f8f9ff !important;
            font-weight: 600;
        }
        
        .struct-row .name-column {
            padding-left: 20px;
        }
        
        .nested-row {
            background-color: #f0f4ff !important;
        }
        
        .nested-row .name-column {
            padding-left: 40px;
            font-weight: normal;
        }
        
        .nested-row .type-column {
            color: #6c757d;
            font-style: italic;
        }
        
        .hidden {
            display: none;
        }
        
        .value-input {
            width: 100%;
            padding: 8px 12px;
            border: 1px solid #ced4da;
            border-radius: 4px;
            font-size: 0.9rem;
            font-family: 'Courier New', monospace;
            transition: all 0.3s ease;
        }
        
        .value-input:focus {
            outline: none;
            border-color: #4b6cb7;
            box-shadow: 0 0 0 2px rgba(75, 108, 183, 0.2);
        }
        
        .value-result {
            padding: 8px 12px;
            background-color: #e9ecef;
            border-radius: 4px;
            border: 1px solid #dee2e6;
            font-family: 'Courier New', monospace;
            min-height: 36px;
            display: flex;
            align-items: center;
            transition: all 0.3s ease;
        }
        
        .value-result.has-value {
            background-color: #d4edda;
            border-color: #c3e6cb;
            color: #155724;
        }
        
        .empty-value {
            color: #6c757d;
            font-style: italic;
        }
        
        .struct-no-data {
            color: #6c757d;
            font-style: italic;
        }
        
        .main-display-panel {
            background-color: #f8f9fa;
            border-radius: 8px;
            padding: 25px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
            margin-bottom: 20px;
        }
        
        .panel-title {
            font-size: 1.3rem;
            color: #2c4a8f;
            margin-bottom: 20px;
            padding-bottom: 15px;
            border-bottom: 2px solid #dee2e6;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        .panel-subtitle {
            font-size: 0.9rem;
            color: #6c757d;
            font-weight: normal;
        }
        
        .upload-section {
            display: flex;
            flex-direction: column;
            align-items: center;
            margin-bottom: 30px;
        }
        
        .file-input-wrapper {
            position: relative;
            width: 100%;
            max-width: 500px;
            margin-bottom: 15px;
        }
        
        .file-input {
            position: absolute;
            left: 0;
            top: 0;
            opacity: 0;
            width: 100%;
            height: 100%;
            cursor: pointer;
        }
        
        .file-input-label {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            width: 100%;
            padding: 40px 20px;
            border: 2px dashed #4b6cb7;
            border-radius: 8px;
            background-color: #f8f9ff;
            transition: all 0.3s ease;
            cursor: pointer;
        }
        
        .file-input-label:hover {
            background-color: #eef1ff;
            border-color: #3a5ca9;
        }
        
        .file-input-label.drag-over {
            background-color: #e1e8ff;
            border-color: #2c4a8f;
        }
        
        .file-icon {
            font-size: 48px;
            color: #4b6cb7;
            margin-bottom: 15px;
        }
        
        .file-input-text {
            font-size: 1.1rem;
            color: #4b6cb7;
            font-weight: 500;
            margin-bottom: 5px;
        }
        
        .file-input-hint {
            font-size: 0.9rem;
            color: #6c757d;
        }
        
        .selected-file {
            margin-top: 15px;
            padding: 10px 15px;
            background-color: #e8f4ff;
            border-radius: 6px;
            width: 100%;
            max-width: 500px;
            display: none;
        }
        
        .selected-file.show {
            display: block;
        }
        
        .file-name {
            font-weight: 500;
            color: #2c4a8f;
        }
        
        .file-size {
            font-size: 0.9rem;
            color: #6c757d;
            margin-top: 5px;
        }
        
        .empty-message {
            color: #6c757d;
            font-style: italic;
            text-align: center;
            padding: 40px 0;
            font-size: 1rem;
        }
        
        .button-group {
            display: flex;
            justify-content: center;
            gap: 15px;
            margin-top: 20px;
            align-items: center;
            flex-wrap: wrap;
        }

        .function-call-section {
            display: flex;
            gap: 10px;
            align-items: center;
            flex: 1;
            max-width: 600px;
        }

        .function-call-section input,
        .function-call-section .btn,
        .button-group .btn {
            height: 44px;
            padding: 10px 15px;
            border: 1px solid #ced4da;
            border-radius: 6px;
            font-size: 0.9rem;
            box-sizing: border-box;
            line-height: 1.5;
        }

        .struct-name-input,
        .function-call-input {
            flex: 1;
            min-width: 180px;
        }

        .struct-name-input:focus,
        .function-call-input:focus {
            outline: none;
            border-color: #4b6cb7;
            box-shadow: 0 0 0 2px rgba(75, 108, 183, 0.2);
        }

        .btn {
            border: none;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.3s ease;
            font-size: 1rem;
            display: flex;
            align-items: center;
            justify-content: center;
            white-space: nowrap;
        }
        
        .btn-primary {
            background-color: #4b6cb7;
            color: white;
        }
        
        .btn-primary:hover {
            background-color: #3a5ca9;
        }
        
        .btn-primary:disabled {
            background-color: #6c757d;
            cursor: not-allowed;
        }
        
        .btn-secondary {
            background-color: #b74b50;
            color: white;
            border: none;
        }

        .btn-secondary:hover {
            background-color: #98074e;
        }
            
        footer {
            text-align: center;
            padding: 15px;
            color: #6c757d;
            font-size: 0.9rem;
            border-top: 1px solid #dee2e6;
        }
        
        .status-message {
            margin-top: 15px;
            padding: 12px;
            border-radius: 6px;
            text-align: center;
            font-weight: 500;
        }
        
        .loading-message {
            background-color: #cce7ff;
            color: #004085;
        }
        
        .success-message {
            background-color: #d4edda;
            color: #155724;
        }
        
        .error-message {
            background-color: #f8d7da;
            color: #721c24;
        }
        
        .query-result-message {
            margin-top: 10px;
            padding: 10px;
            border-radius: 6px;
            text-align: center;
            font-weight: 500;
        }
        
        .query-success {
            background-color: #d1ecf1;
            color: #0c5460;
            border: 1px solid #bee5eb;
        }
        
        .query-error {
            background-color: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        
        @media (max-width: 768px) {
            .table-container {
                overflow-x: auto;
            }
            
            .variable-table {
                min-width: 700px;
            }
            
            .panel-title {
                flex-direction: column;
                align-items: flex-start;
                gap: 10px;
            }
            
            .connection-section {
                flex-direction: column;
                align-items: stretch;
            }
            
            .function-section {
                flex-direction: column;
                align-items: stretch;
            }
            
            .query-section {
                flex-direction: column;
                align-items: stretch;
            }
            
            .query-input {
                max-width: 100%;
            }
            
            .button-group {
                flex-direction: column;
                align-items: stretch;
            }
            
            .function-call-section {
                max-width: 100%;
                flex-direction: column;
            }
            
            .function-call-section input,
            .function-call-section .btn,
            .button-group .btn {
                height: 44px;
                width: 100%;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>接口调用测试工具</h1>
            <p class="subtitle">连接Linux服务端并调用接口函数</p>
        </header>
        
        <div class="content">
            <div class="connection-section">
                <input type="text" id="linuxHost" class="connection-input" placeholder="Linux服务器IP" value="10.120.14.163">
                <input type="text" id="linuxPort" class="connection-input" placeholder="Linux服务器端口" value="8081">
                <button id="connectLinuxBtn" class="connection-btn">连接Linux</button>
                <button id="disconnectLinuxBtn" class="connection-btn disconnected" style="display: none;">断开Linux</button>
                <div id="linuxStatus" class="connection-status status-disconnected">
                    <div class="status-indicator"></div>
                    <span>未连接Linux</span>
                </div>
            </div>
            
            <div class="upload-section">
                <div class="file-input-wrapper">
                    <input type="file" id="fileInput" class="file-input" accept=".h">
                    <label for="fileInput" class="file-input-label" id="fileInputLabel">
                        <div class="file-icon">📁</div>
                        <div class="file-input-text">点击选择文件或拖放文件到此处</div>
                        <div class="file-input-hint">支持C/C++头文件(.h)格式</div>
                    </label>
                </div>
                
                <div class="selected-file" id="selectedFile">
                    <div class="file-name" id="fileName">文件名</div>
                    <div class="file-size" id="fileSize">文件大小</div>
                </div>
            </div>
            
            <div class="main-display-panel">
                <h2 class="panel-title">
                    变量信息
                    <span class="panel-subtitle">序号 | 变量名称 | 变量类型 | 变量值</span>
                </h2>
                <div class="table-container" id="mainTableContainer">
                    <div class="empty-message">请上传.h头文件以显示变量信息</div>
                </div>
            </div>
            
            <div class="button-group">
                <div class="function-call-section">
                    <input type="text" id="structName" class="struct-name-input" placeholder="请输入主结构体名称" value="">
                    <input type="text" id="functionName" class="function-call-input" placeholder="请输入函数名" value="">
                    <button class="btn btn-primary" id="callInterfaceBtn" disabled>调用接口</button>
                </div>
                <button class="btn btn-secondary" id="clearBtn">清除</button>
            </div>
        </div>
        
        <footer>
            <p>© 2025 接口调用测试工具</p>
        </footer>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 获取DOM元素
            const linuxHost = document.getElementById('linuxHost');
            const linuxPort = document.getElementById('linuxPort');
            const connectLinuxBtn = document.getElementById('connectLinuxBtn');
            const disconnectLinuxBtn = document.getElementById('disconnectLinuxBtn');
            const linuxStatus = document.getElementById('linuxStatus');
            const structName = document.getElementById('structName');
            const functionName = document.getElementById('functionName');
            const fileInput = document.getElementById('fileInput');
            const fileInputLabel = document.getElementById('fileInputLabel');
            const selectedFile = document.getElementById('selectedFile');
            const fileName = document.getElementById('fileName');
            const fileSize = document.getElementById('fileSize');
            const mainTableContainer = document.getElementById('mainTableContainer');
            const callInterfaceBtn = document.getElementById('callInterfaceBtn');
            const clearBtn = document.getElementById('clearBtn');
            
            let currentFile = null;
            let variables = [];
            let ws = null;
            let isLinuxConnected = false;
            let queryInput = null;
            let rowCounter = 1;
            let currentMessageType = null; // 'loading', 'success', 'error'
            
            // 清除状态消息的函数
            function clearStatusMessages() {
                currentMessageType = null;
                const statusMessages = mainTableContainer.querySelectorAll('.status-message');
                statusMessages.forEach(msg => msg.remove());
            }
            
            // 显示加载消息
            function showLoadingMessage(message) {
                clearStatusMessages();
                currentMessageType = 'loading';
                const loadingMsg = document.createElement('div');
                loadingMsg.className = 'status-message loading-message';
                loadingMsg.textContent = message;
                mainTableContainer.appendChild(loadingMsg);
            }
            
            // 显示成功消息
            function showSuccessMessage(message) {
                clearStatusMessages();
                currentMessageType = 'success';
                const successMsg = document.createElement('div');
                successMsg.className = 'status-message success-message';
                successMsg.textContent = message;
                mainTableContainer.appendChild(successMsg);
            }
            
            // 显示错误消息
            function showErrorMessage(message) {
                clearStatusMessages();
                currentMessageType = 'error';
                const errorMsg = document.createElement('div');
                errorMsg.className = 'status-message error-message';
                errorMsg.textContent = message;
                mainTableContainer.appendChild(errorMsg);
            }
            
            // 连接Linux服务端
            connectLinuxBtn.addEventListener('click', function() {
                connectToLinux();
            });
            
            // 断开Linux连接
            disconnectLinuxBtn.addEventListener('click', function() {
                disconnectFromLinux();
            });
            
            function connectToLinux() {
                const host = linuxHost.value.trim();
                const port = parseInt(linuxPort.value.trim());
                
                if (!host || !port) {
                    alert('请输入Linux服务器的IP地址和端口');
                    return;
                }
                
                // 更新连接状态
                updateLinuxStatus(false, '连接中...');
                
                // 通过WebSocket连接到Windows服务端(Windows服务端会处理与Linux的TCP连接)
                try {
                    ws = new WebSocket('ws://localhost:3000');
                    
                    ws.onopen = function() {
                        console.log('WebSocket连接成功');
                        // 发送连接Linux的请求
                        ws.send(JSON.stringify({
                            type: 'connect_linux',
                            host: host,
                            port: port
                        }));
                    };
                    
                    ws.onmessage = function(event) {
                        const data = JSON.parse(event.data);
                        console.log('接收到WebSocket消息:', data);
                        
                        if (data.type === 'status') {
                            isLinuxConnected = data.linuxConnected;
                            if (isLinuxConnected) {
                                clearStatusMessages();
                                updateLinuxStatus(true, '已连接到Linux服务端');
                                callInterfaceBtn.disabled = false;
                            } else {
                                updateLinuxStatus(false, data.message || '未连接Linux');
                                callInterfaceBtn.disabled = true;
                            }
                        } else if (data.type === 'interface_call_sent') {
                            // 保持当前的加载消息,不需要更新
                            console.log('接口调用指令已发送到Linux服务端');
                        } else if (data.type === 'interface_call_complete') {
                            // 清除加载消息并显示成功
                            clearStatusMessages();
                            
                            // 如果有数据,更新变量值
                            if (data.data) {
                                updateVariableValues(data.data);
                            }
                            
                            // 使用服务端返回的消息
                            showSuccessMessage(data.message || '接口调用完成');
                        } else if (data.type === 'error') {
                            // 清除加载消息并显示错误
                            clearStatusMessages();
                            showErrorMessage('错误: ' + data.message);
                        }
                    };
                    
                    ws.onclose = function() {
                        isLinuxConnected = false;
                        updateLinuxStatus(false, '连接已断开');
                        callInterfaceBtn.disabled = true;
                    };
                    
                    ws.onerror = function(error) {
                        console.error('WebSocket错误:', error);
                        updateLinuxStatus(false, '连接失败');
                        alert('连接失败,请确保Windows服务端正在运行');
                    };
                    
                } catch (error) {
                    console.error('连接错误:', error);
                    alert('连接失败: ' + error.message);
                }
            }
            
            function disconnectFromLinux() {
                if (ws) {
                    ws.send(JSON.stringify({
                        type: 'disconnect_linux'
                    }));
                    ws.close();
                    ws = null;
                }
                isLinuxConnected = false;
                updateLinuxStatus(false, '未连接Linux');
                callInterfaceBtn.disabled = true;
            }
            
            function updateLinuxStatus(connected, message) {
                if (connected) {
                    linuxStatus.className = 'connection-status status-connected';
                    connectLinuxBtn.style.display = 'none';
                    disconnectLinuxBtn.style.display = 'block';
                } else {
                    linuxStatus.className = 'connection-status status-disconnected';
                    connectLinuxBtn.style.display = 'block';
                    disconnectLinuxBtn.style.display = 'none';
                }
                linuxStatus.querySelector('span').textContent = message;
            }
            
            // 处理文件选择 - 添加结构体名称检查
            fileInput.addEventListener('change', function(e) {
                if (e.target.files.length > 0) {
                    // 检查是否已输入结构体名称
                    if (!structName.value.trim()) {
                        alert('请先输入主结构体名称');
                        fileInput.value = ''; // 清除文件选择
                        return;
                    }
                    handleFile(e.target.files[0]);
                }
            });
            
            // 处理拖放功能 - 添加结构体名称检查
            fileInputLabel.addEventListener('dragover', function(e) {
                e.preventDefault();
                fileInputLabel.classList.add('drag-over');
            });
            
            fileInputLabel.addEventListener('dragleave', function() {
                fileInputLabel.classList.remove('drag-over');
            });
            
            fileInputLabel.addEventListener('drop', function(e) {
                e.preventDefault();
                fileInputLabel.classList.remove('drag-over');
                
                if (e.dataTransfer.files.length > 0) {
                    // 检查是否已输入结构体名称
                    if (!structName.value.trim()) {
                        alert('请先输入主结构体名称');
                        return;
                    }
                    handleFile(e.dataTransfer.files[0]);
                }
            });
            
            // 处理文件
            function handleFile(file) {
                currentFile = file;
                
                // 显示文件信息
                fileName.textContent = file.name;
                fileSize.textContent = formatFileSize(file.size);
                selectedFile.classList.add('show');
                
                // 读取并显示文件内容
                const reader = new FileReader();
                reader.onload = function(e) {
                    // 提取结构体变量
                    variables = extractStructVariables(e.target.result);
                    
                    if (variables && variables.length > 0) {
                        // 创建变量表格
                        createMainTable(variables);
                        // 创建查询输入框
                        createQueryInput();
                    } else {
                        mainTableContainer.innerHTML = '<div class="empty-message">未找到结构体变量</div>';
                    }
                };
                
                // 读取文件
                reader.readAsText(file);
            }
            
            // 格式化文件大小
            function formatFileSize(bytes) {
                if (bytes === 0) return '0 Bytes';
                
                const k = 1024;
                const sizes = ['Bytes', 'KB', 'MB', 'GB'];
                const i = Math.floor(Math.log(bytes) / Math.log(k));
                
                return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
            }
            
            // 提取结构体变量 - 优化后的解析逻辑
            function extractStructVariables(content) {
                console.log("开始解析头文件内容...");
                
                // 移除注释和预处理指令
                let cleanContent = content
                    .replace(/\/\*[\s\S]*?\*\//g, '')  // 移除多行注释
                    .replace(/\/\/.*$/gm, '')          // 移除单行注释
                    .replace(/#.*$/gm, '')             // 移除预处理指令
                    .replace(/\r\n/g, '\n')            // 统一换行符
                    .trim();
                
                console.log("清理后的内容:", cleanContent);
                
                // 存储所有结构体定义
                const structs = new Map();
                
                // 1. 首先解析所有结构体定义
                parseAllStructs(cleanContent, structs);
                
                console.log("解析到的结构体:", structs);
                
                if (structs.size === 0) {
                    console.log("未找到任何结构体定义");
                    return null;
                }
                
                // 2. 从输入框获取主结构体名称
                const mainStructName = structName.value.trim();
                if (!mainStructName) {
                    console.log("请输入结构体名称");
                    alert("请在结构体名称输入框中输入要解析的结构体名称");
                    return null;
                }
                
                if (!structs.has(mainStructName)) {
                    console.log("未找到指定的主结构体: " + mainStructName);
                    alert("未找到指定的结构体: " + mainStructName + "\n\n可用的结构体有: " + Array.from(structs.keys()).join(', '));
                    return null;
                }
                
                console.log("主结构体:", mainStructName);
                
                // 3. 构建变量树
                const result = buildVariableTree(mainStructName, structs);
                console.log("构建的变量树:", result);
                
                return result;
            }
            
            // 解析所有结构体
            function parseAllStructs(content, structs) {
                // 解析 typedef struct 定义
                const typedefPattern = /typedef\s+struct\s+(\w+)\s*\{([^}]+)\}\s*(\w+)\s*;/g;
                let match;
                
                while ((match = typedefPattern.exec(content)) !== null) {
                    const structName = match[1];
                    const structBody = match[2];
                    const aliasName = match[3];
                    
                    const members = parseStructMembers(structBody);
                    structs.set(structName, members);
                    structs.set(aliasName, members); // 同时存储别名
                    
                    console.log(`找到typedef结构体: ${structName} (别名: ${aliasName})`, members);
                }
                
                // 解析 struct 定义
                const structPattern = /struct\s+(\w+)\s*\{([^}]+)\}\s*(\w+)?\s*;/g;
                
                while ((match = structPattern.exec(content)) !== null) {
                    const structName = match[1];
                    const structBody = match[2];
                    const aliasName = match[3];
                    
                    const members = parseStructMembers(structBody);
                    structs.set(structName, members);
                    if (aliasName) {
                        structs.set(aliasName, members);
                    }
                    
                    console.log(`找到struct结构体: ${structName}`, members);
                }
            }
            
            // 解析结构体成员
            function parseStructMembers(structBody) {
                const members = [];
                const lines = structBody.split('\n');
                
                for (let line of lines) {
                    line = line.trim();
                    if (!line || line === '{' || line === '}') continue;
                    
                    // 匹配类型和变量名,支持指针和数组
                    const memberPattern = /(\w+(?:\s*\*\s*)?)\s+(\w+)(?:\s*\[[^\]]*\])?\s*;/;
                    const match = line.match(memberPattern);
                    
                    if (match) {
                        members.push({
                            type: match[1].trim(),
                            name: match[2].trim()
                        });
                    }
                }
                
                return members;
            }
            
            // 构建变量树
            function buildVariableTree(mainStructName, structs) {
                const result = [];
                const mainStructMembers = structs.get(mainStructName);

                if (!mainStructMembers) return result;

                function createVariableNode(member, parentName = '', depth = 0) {
                    // 对于显示名称,如果是嵌套变量,只显示变量名本身
                    // 对于数据名称,保持完整路径用于接口调用
                    const displayName = depth > 0 ? member.name : (parentName ? `${parentName}.${member.name}` : member.name);
                    const dataName = parentName ? `${parentName}.${member.name}` : member.name;
                    const memberType = member.type.replace(/\s*\*\s*/g, '').trim();
                    
                    // 检查是否是结构体类型
                    if (structs.has(memberType)) {
                        // 结构体成员
                        const node = {
                            name: displayName,        // 显示名称(去掉前缀)
                            dataName: dataName,       // 完整路径(用于接口调用)
                            type: memberType,
                            isStruct: true,
                            depth: depth,
                            children: []
                        };
                        
                        console.log(`发现结构体成员: ${dataName} (显示: ${displayName}), 深度: ${depth}`);
                        
                        // 递归处理嵌套结构体的所有成员
                        const nestedMembers = structs.get(memberType);
                        nestedMembers.forEach(nestedMember => {
                            console.log(`处理嵌套成员: ${nestedMember.name} 在 ${dataName} 中`);
                            const childNode = createVariableNode(nestedMember, dataName, depth + 1);
                            if (childNode) {
                                node.children.push(childNode);
                            }
                        });
                        
                        return node;
                    } else {
                        // 普通成员变量
                        console.log(`普通成员: ${dataName} (显示: ${displayName}), 深度: ${depth}`);
                        return {
                            name: displayName,        // 显示名称(去掉前缀)
                            dataName: dataName,       // 完整路径(用于接口调用)
                            type: member.type,
                            isStruct: false,
                            depth: depth,
                            children: null
                        };
                    }
                }

                // 处理主结构体的所有成员
                mainStructMembers.forEach(member => {
                    console.log(`处理主结构体成员: ${member.name} (类型: ${member.type})`);
                    const node = createVariableNode(member, '', 0);
                    if (node) {
                        result.push(node);
                    }
                });
        
                console.log("最终构建的变量树:", result);
                return result;
            }

            function createMainTable(variables) {
                const table = document.createElement('table');
                table.className = 'variable-table';
            
                // 创建表头
                const thead = document.createElement('thead');
                thead.innerHTML = `
                <tr>
                    <th>序号</th>
                    <th>变量名称</th>
                    <th>变量类型</th>
                    <th>变量值</th>
                </tr>
                `;
                table.appendChild(thead);
                    
                // 创建表体
                const tbody = document.createElement('tbody');
                let sequenceNumber = 1;
                
                function createTopLevelRows(nodes) {
                    nodes.forEach(node => {
                        // 顶级变量行
                        const topRow = document.createElement('tr');
                        topRow.dataset.depth = 0;
                        topRow.dataset.variableName = node.name; // 添加变量名数据属性
                        
                        if (node.isStruct) {
                            topRow.classList.add('struct-row');
                        }
                        
                        // 序号列 - 只为顶级变量分配序号
                        const indexCell = document.createElement('td');
                        indexCell.className = 'index-column';
                        indexCell.textContent = sequenceNumber;
                        
                        // 名称列
                        const nameCell = document.createElement('td');
                        nameCell.className = 'name-column';
                        
                        if (node.isStruct) {
                            // 结构体变量添加折叠按钮
                            const toggleBtn = document.createElement('button');
                            toggleBtn.className = 'toggle-btn';
                            toggleBtn.innerHTML = '▶';
                            toggleBtn.dataset.expanded = 'false';
                            nameCell.appendChild(toggleBtn);
                        }
                        
                        const nameSpan = document.createElement('span');
                        nameSpan.textContent = node.name;
                        nameCell.appendChild(nameSpan);
                        
                        // 类型列
                        const typeCell = document.createElement('td');
                        typeCell.className = 'type-column';
                        typeCell.textContent = node.type;
                        
                        // 值列 - 优化结构体变量的显示
                        const valueCell = document.createElement('td');
                        valueCell.className = 'value-column';
                        const resultDiv = document.createElement('div');
                        
                        if (node.isStruct) {
                            // 结构体变量显示"结构体"而不是"无数据"
                            resultDiv.className = 'value-result struct-no-data';
                            resultDiv.textContent = '结构体';
                        } else {
                            resultDiv.className = 'value-result empty-value';
                            resultDiv.textContent = '等待调用接口...';
                        }
                        
                        resultDiv.dataset.variableName = node.dataName;
                        valueCell.appendChild(resultDiv);
                        
                        topRow.appendChild(indexCell);
                        topRow.appendChild(nameCell);
                        topRow.appendChild(typeCell);
                        topRow.appendChild(valueCell);
                        tbody.appendChild(topRow);
                        
                        // 如果当前节点是结构体且有子节点,创建嵌套行
                        if (node.isStruct && node.children && node.children.length > 0) {
                            createNestedRows(node.children, sequenceNumber, 1, topRow);
                        }
                        
                        sequenceNumber++;
                    });
                }
        
                function createNestedRows(children, parentSequenceNumber, currentDepth, parentRow) {
                    const parentIndex = Array.from(parentRow.parentNode.children).indexOf(parentRow);
                    
                    children.forEach(child => {
                        const nestedRow = document.createElement('tr');
                        nestedRow.classList.add('nested-row');
                        nestedRow.classList.add('hidden');
                        nestedRow.dataset.parentIndex = parentIndex + 1; // 使用行索引而不是序号
                        nestedRow.dataset.depth = currentDepth;
                        nestedRow.dataset.variableName = child.name; // 添加变量名数据属性
                        
                        // 序号列 - 嵌套变量不显示序号
                        const indexCell = document.createElement('td');
                        indexCell.className = 'index-column';
                        indexCell.textContent = '';
                        
                        // 名称列 - 嵌套变量添加缩进
                        const nameCell = document.createElement('td');
                        nameCell.className = 'name-column';
                        
                        // 根据深度计算缩进
                        const indent = document.createElement('span');
                        indent.style.marginLeft = (currentDepth * 20) + 'px';
                        nameCell.appendChild(indent);
                        
                        if (child.isStruct) {
                            // 嵌套的结构体变量添加折叠按钮
                            const toggleBtn = document.createElement('button');
                            toggleBtn.className = 'toggle-btn';
                            toggleBtn.innerHTML = '▶';
                            toggleBtn.dataset.expanded = 'false';
                            nameCell.appendChild(toggleBtn);
                        }
                        
                        const nameSpan = document.createElement('span');
                        nameSpan.textContent = child.name;
                        nameCell.appendChild(nameSpan);
                        
                        // 类型列
                        const typeCell = document.createElement('td');
                        typeCell.className = 'type-column';
                        typeCell.textContent = child.type;
                        
                        // 值列 - 优化结构体变量的显示
                        const valueCell = document.createElement('td');
                        valueCell.className = 'value-column';
                        const resultDiv = document.createElement('div');
                        
                        if (child.isStruct) {
                            // 结构体变量显示"结构体"而不是"无数据"
                            resultDiv.className = 'value-result struct-no-data';
                            resultDiv.textContent = '结构体';
                        } else {
                            resultDiv.className = 'value-result empty-value';
                            resultDiv.textContent = '等待调用接口...';
                        }
                        
                        resultDiv.dataset.variableName = child.dataName;
                        valueCell.appendChild(resultDiv);
                        
                        nestedRow.appendChild(indexCell);
                        nestedRow.appendChild(nameCell);
                        nestedRow.appendChild(typeCell);
                        nestedRow.appendChild(valueCell);
                        tbody.appendChild(nestedRow);
                        
                        // 如果当前子节点也是结构体且有子节点,继续递归创建
                        if (child.isStruct && child.children && child.children.length > 0) {
                            createNestedRows(child.children, parentSequenceNumber, currentDepth + 1, nestedRow);
                        }
                    });
                }
                
                createTopLevelRows(variables);
                table.appendChild(tbody);
                mainTableContainer.innerHTML = '';
                mainTableContainer.appendChild(table);
                
                // 设置折叠按钮事件
                setupToggleButtons();
            }

            function setupToggleButtons() {
                const toggleButtons = mainTableContainer.querySelectorAll('.toggle-btn');
                toggleButtons.forEach(button => {
                    button.onclick = function() {
                        const parentRow = this.closest('tr');
                        const isExpanded = this.dataset.expanded === 'true';
                        
                        if (isExpanded) {
                            // 隐藏直接子行
                            this.innerHTML = '▶';
                            this.dataset.expanded = 'false';
                            hideDirectChildren(parentRow);
                        } else {
                            // 显示直接子行
                            this.innerHTML = '▼';
                            this.dataset.expanded = 'true';
                            showDirectChildren(parentRow);
                        }
                    };
                });
            }

            // 显示直接子行
            function showDirectChildren(parentRow) {
                const parentIndex = Array.from(parentRow.parentNode.children).indexOf(parentRow);
                const parentDepth = parseInt(parentRow.dataset.depth || 0);
                
                // 只显示直接子行(深度+1)
                const allRows = mainTableContainer.querySelectorAll('tr.nested-row');
                allRows.forEach(row => {
                    const rowParentIndex = parseInt(row.dataset.parentIndex);
                    const rowDepth = parseInt(row.dataset.depth);
                    if (rowParentIndex === parentIndex + 1 && rowDepth === parentDepth + 1) {
                        row.classList.remove('hidden');
                    }
                });
            }

            // 隐藏直接子行
            function hideDirectChildren(parentRow) {
                const parentIndex = Array.from(parentRow.parentNode.children).indexOf(parentRow);
                const parentDepth = parseInt(parentRow.dataset.depth || 0);
                
                // 隐藏所有子行(包括嵌套的子行)
                const allRows = mainTableContainer.querySelectorAll('tr.nested-row');
                allRows.forEach(row => {
                    const rowParentIndex = parseInt(row.dataset.parentIndex);
                    const rowDepth = parseInt(row.dataset.depth);
                    if (rowParentIndex === parentIndex + 1 && rowDepth > parentDepth) {
                        row.classList.add('hidden');
                        // 同时重置子行的折叠按钮状态
                        const childToggleBtn = row.querySelector('.toggle-btn');
                        if (childToggleBtn) {
                            childToggleBtn.innerHTML = '▶';
                            childToggleBtn.dataset.expanded = 'false';
                        }
                    }
                });
            }
            
            // 更新变量值 - 优化结构体变量的显示
            function updateVariableValues(interfaceData) {
                const valueElements = mainTableContainer.querySelectorAll('.value-result');
                
                valueElements.forEach(element => {
                    const variableName = element.dataset.variableName;
                    
                    // 检查是否是结构体变量
                    const isStruct = element.classList.contains('struct-no-data');
                    
                    if (interfaceData && interfaceData[variableName] !== undefined) {
                        // 有数据的情况
                        element.textContent = interfaceData[variableName];
                        element.classList.remove('empty-value');
                        element.classList.add('has-value');
                        if (isStruct) {
                            element.classList.remove('struct-no-data');
                        }
                    } else {
                        // 无数据的情况
                        if (isStruct) {
                            // 结构体变量保持"结构体"显示
                            element.textContent = '结构体';
                            element.className = 'value-result struct-no-data';
                        } else {
                            element.textContent = '无数据';
                            element.className = 'value-result empty-value';
                        }
                    }
                });
            }
            
            // 创建查询输入框
            function createQueryInput() {
                const querySection = document.createElement('div');
                querySection.className = 'query-section';
                
                queryInput = document.createElement('input');
                queryInput.type = 'text';
                queryInput.className = 'query-input';
                queryInput.placeholder = '输入变量名称进行查询...';
                
                const queryBtn = document.createElement('button');
                queryBtn.className = 'query-btn';
                queryBtn.textContent = '查询';
                queryBtn.onclick = handleQueryVariable;
                
                querySection.appendChild(queryInput);
                querySection.appendChild(queryBtn);
                
                // 将查询区域插入到表格容器之前
                const mainDisplayPanel = document.querySelector('.main-display-panel');
                const tableContainer = mainDisplayPanel.querySelector('.table-container');
                mainDisplayPanel.insertBefore(querySection, tableContainer);
            }
            
            // 处理变量查询 - 增强支持嵌套结构体查找
            function handleQueryVariable() {
                if (!queryInput) return;
                
                const variableName = queryInput.value.trim();
                
                if (!variableName) {
                    showQueryMessage('请输入要查询的变量名称', 'error');
                    return;
                }
                
                if (!variables || variables.length === 0) {
                    showQueryMessage('请先上传头文件以加载变量信息', 'error');
                    return;
                }
                
                // 清除之前的高亮和消息
                clearHighlights();
                clearQueryMessage();
                
                // 递归查找变量(不区分大小写)
                const foundVariables = [];
                findVariableRecursive(variables, variableName.toLowerCase(), foundVariables);
                
                if (foundVariables.length > 0) {
                    // 高亮显示找到的所有变量
                    let firstFound = false;
                    foundVariables.forEach(variable => {
                        const highlighted = highlightVariable(variable.name);
                        if (highlighted && !firstFound) {
                            firstFound = true;
                        }
                    });
                    
                    // 显示找到的数量
                    showQueryMessage(`找到 ${foundVariables.length} 个匹配的变量`, 'success');
                } else {
                    showQueryMessage(`未找到变量: "${variableName}"`, 'error');
                }
            }
            
            // 递归查找变量
            function findVariableRecursive(nodes, searchName, results) {
                nodes.forEach(node => {
                    // 检查当前节点是否匹配
                    if (node.name.toLowerCase().includes(searchName)) {
                        results.push(node);
                    }
                    
                    // 递归查找子节点
                    if (node.children && node.children.length > 0) {
                        findVariableRecursive(node.children, searchName, results);
                    }
                });
            }
            
            // 高亮显示变量 - 修复高亮和滚动问题
            function highlightVariable(variableName) {
                const rows = mainTableContainer.querySelectorAll('.variable-table tr');
                let found = false;
                
                rows.forEach(row => {
                    // 使用数据属性来匹配变量名,更可靠
                    if (row.dataset.variableName === variableName) {
                        row.classList.add('highlighted');
                        found = true;
                        
                        // 展开所有父级结构体
                        expandParentRows(row);
                        
                        // 滚动到高亮行 - 添加延迟确保DOM更新完成
                        setTimeout(() => {
                            row.scrollIntoView({ 
                                behavior: 'smooth', 
                                block: 'center' 
                            });
                        }, 100);
                    }
                });
                
                return found;
            }
            
            // 展开所有父级行
            function expandParentRows(row) {
                let currentRow = row;
                while (currentRow) {
                    const parentIndex = currentRow.dataset.parentIndex;
                    if (parentIndex) {
                        const parentRow = mainTableContainer.querySelectorAll('.variable-table tr')[parentIndex - 1];
                        if (parentRow) {
                            const toggleBtn = parentRow.querySelector('.toggle-btn');
                            if (toggleBtn && toggleBtn.dataset.expanded === 'false') {
                                // 模拟点击展开按钮
                                toggleBtn.click();
                            }
                            currentRow = parentRow;
                        } else {
                            break;
                        }
                    } else {
                        break;
                    }
                }
            }
            
            // 清除高亮
            function clearHighlights() {
                const highlightedRows = mainTableContainer.querySelectorAll('.variable-table tr.highlighted');
                highlightedRows.forEach(row => {
                    row.classList.remove('highlighted');
                });
            }
            
            // 显示查询消息
            function showQueryMessage(message, type) {
                clearQueryMessage();
                
                const messageDiv = document.createElement('div');
                messageDiv.className = `query-result-message query-${type}`;
                messageDiv.textContent = message;
                
                const querySection = document.querySelector('.query-section');
                if (querySection) {
                    querySection.appendChild(messageDiv);
                }
            }
            
            // 清除查询消息
            function clearQueryMessage() {
                const existingMessage = document.querySelector('.query-result-message');
                if (existingMessage) {
                    existingMessage.remove();
                }
            }
            
            // 调用接口
            callInterfaceBtn.addEventListener('click', function() {
                if (!currentFile) {
                    alert('请先选择一个文件');
                    return;
                }
                
                if (!isLinuxConnected) {
                    alert('请先连接到Linux服务端');
                    return;
                }
                
                const funcName = functionName.value.trim();
                if (!funcName) {
                    alert('请输入要调用的函数名');
                    return;
                }
                
                // 清除之前的状态消息
                clearStatusMessages();
                
                // 显示加载状态 - 修改为更准确的消息
                showLoadingMessage(`正在调用函数 "${funcName}",等待响应...`);
                
                // 通过WebSocket发送请求
                try {
                    ws.send(JSON.stringify({
                        type: 'interface_call',
                        function_name: funcName,
                        variables: {} // 这里可以根据需要传递参数
                    }));
                } catch (error) {
                    console.error('发送请求失败:', error);
                    // 清除加载消息并显示错误
                    clearStatusMessages();
                    showErrorMessage('发送请求失败: ' + error.message);
                }
            });
            
            // 清除内容
            clearBtn.addEventListener('click', function() {
                fileInput.value = '';
                selectedFile.classList.remove('show');
                mainTableContainer.innerHTML = '<div class="empty-message">请上传.h头文件以显示变量信息</div>';
                currentFile = null;
                variables = [];
                structName.value = ''; // 清除结构体名称
                
                // 清除查询输入框
                const querySection = document.querySelector('.query-section');
                if (querySection) {
                    querySection.remove();
                }
                queryInput = null;
                clearQueryMessage();
            });
        });
    </script>
</body>
</html>

sever.js

javascript 复制代码
const net = require('net');
const http = require('http');
const fs = require('fs');
const path = require('path');

class LinuxTCPClient {
    constructor(host, port) {
        this.host = host || '';
        this.port = port || 0;
        this.client = null;
        this.isConnected = false;
        this.responseHandlers = new Map();
        this.buffer = '';
        this.totalReceived = 0;
        this.packetCount = 0;
        this.messageCount = 0;
        this.expectedLength = 0;
        // 新增:存储请求ID与WebSocket的映射
        this.requestWsMap = new Map();
    }

    connect() {
        return new Promise((resolve, reject) => {
            if (!this.host || !this.port) {
                reject(new Error('未设置Linux服务器地址或端口'));
                return;
            }
            
            this.client = new net.Socket();
            
            this.client.connect(this.port, this.host, () => {
                this.isConnected = true;
                console.log(`已连接到Linux服务端 ${this.host}:${this.port}`);
                resolve();
            });

            this.client.on('data', (data) => {
                const message = data.toString();
                this.packetCount++;
                this.totalReceived += message.length;
                console.log(`从Linux服务端接收数据片段,长度: ${message.length}, 累计: ${this.totalReceived}字节, 包数: ${this.packetCount}`);
                
                // 累积数据到缓冲区
                this.buffer += message;
                
                // 尝试处理缓冲区中的完整消息
                this.processBuffer();
            });

            this.client.on('close', () => {
                this.isConnected = false;
                this.buffer = '';
                this.totalReceived = 0;
                this.packetCount = 0;
                this.messageCount = 0;
                this.expectedLength = 0;
                console.log('与Linux服务端的连接已断开');
                this.responseHandlers.forEach((handler, requestId) => {
                    handler.reject(new Error('连接已断开'));
                });
                this.responseHandlers.clear();
                this.requestWsMap.clear();
            });

            this.client.on('error', (error) => {
                console.error('连接Linux服务端失败:', error);
                reject(error);
            });
        });
    }

    processBuffer() {
        let processedCount = 0;
        
        // 首先检查是否已经有完整的JSON消息
        while (this.buffer.length > 0) {
            const firstBrace = this.buffer.indexOf('{');
            if (firstBrace === -1) {
                console.log('缓冲区中没有找到JSON起始标记,等待更多数据');
                break;
            }
            
            // 从第一个 { 开始查找匹配的 }
            let braceCount = 0;
            let endIndex = -1;
            
            for (let i = firstBrace; i < this.buffer.length; i++) {
                if (this.buffer[i] === '{') {
                    braceCount++;
                } else if (this.buffer[i] === '}') {
                    braceCount--;
                    if (braceCount === 0) {
                        endIndex = i;
                        break;
                    }
                }
            }
            
            if (endIndex === -1) {
                break;
            }
            
            // 提取完整的JSON消息
            const completeMessage = this.buffer.substring(firstBrace, endIndex + 1);
            this.buffer = this.buffer.substring(endIndex + 1);
            
            processedCount++;
            this.messageCount++;
            
            console.log(`接收到的报文: ${completeMessage}`);
            this.handleResponse(completeMessage);
            
            if (processedCount > 5) {
                console.warn('处理消息过多,暂停处理');
                break;
            }
        }
        
        if (processedCount > 0) {
            console.log(`本轮处理了 ${processedCount} 条完整消息,总计: ${this.messageCount} 条`);
        }
        
        this.monitorBuffer();
    }

    monitorBuffer() {
        const bufferSize = this.buffer.length;
        if (bufferSize > 1024 * 1024) {
            console.warn(`缓冲区过大: ${bufferSize}字节,清空缓冲区`);
            this.buffer = '';
        }
    }

    disconnect() {
        if (this.client) {
            this.client.destroy();
            this.client = null;
            this.isConnected = false;
            this.buffer = '';
            this.totalReceived = 0;
            this.packetCount = 0;
            this.messageCount = 0;
            this.expectedLength = 0;
            this.responseHandlers.forEach((handler, requestId) => {
                handler.reject(new Error('主动断开连接'));
            });
            this.responseHandlers.clear();
            this.requestWsMap.clear();
        }
    }

    sendInterfaceCall(functionName, variables, ws = null) {
        return new Promise((resolve, reject) => {
            if (!this.isConnected) {
                reject(new Error('未连接到Linux服务端'));
                return;
            }

            const requestId = Date.now().toString();
            const request = {
                type: 'interface_call',
                function_name: functionName,
                parameters: variables,
                requestId: requestId,
                timestamp: new Date().toISOString()
            };

            this.responseHandlers.set(requestId, { resolve, reject });
            
            // 存储请求ID与WebSocket的映射
            if (ws) {
                this.requestWsMap.set(requestId, ws);
                console.log(`存储请求映射: ${requestId} -> WebSocket`);
            }

            const message = JSON.stringify(request);
            console.log('发送请求到Linux服务端:', message);
            this.client.write(message + '\n');
        });
    }

    handleResponse(message) {
        if (!message || message.trim() === '') {
            console.log('跳过空消息');
            return;
        }
        
        try {
            console.log(`开始解析JSON,消息长度: ${message.length}`);
            
            const response = JSON.parse(message);
            console.log('✅ JSON解析成功!');
            console.log(`响应类型: ${response.type}`);
            console.log(`是否有requestId: ${!!response.requestId}`);
            // console.log(`响应数据:`, response.data);
            
            if (response.type === 'error') {
                console.log('接收到错误响应:', response.message);
                
                if (response.requestId) {
                    const handler = this.responseHandlers.get(response.requestId);
                    const ws = this.requestWsMap.get(response.requestId);
                    
                    if (handler) {
                        this.responseHandlers.delete(response.requestId);
                        handler.reject(new Error(response.message));
                    }
                    
                    // 给发起请求的特定客户端发送错误消息
                    if (ws && ws.readyState === ws.OPEN) {
                        console.log(`发送错误消息给特定客户端: ${response.message}`);
                        ws.send(JSON.stringify({
                            type: 'error',
                            message: response.message,
                            requestId: response.requestId
                        }));
                    }
                    
                    // 清理映射
                    this.requestWsMap.delete(response.requestId);
                }
                return;
            }

            if (response.type === 'interface_response') {
                console.log('✅ 接收到接口响应数据');
                
                if (response.requestId) {
                    const handler = this.responseHandlers.get(response.requestId);
                    const ws = this.requestWsMap.get(response.requestId);
                    
                    if (handler) {
                        this.responseHandlers.delete(response.requestId);
                        console.log('✅ 接口调用Promise已解决');
                        handler.resolve(response.data);
                    }
                    
                    // 修改:只给发起请求的特定客户端发送成功消息
                    if (ws && ws.readyState === ws.OPEN) {
                        console.log(`发送接口调用完成消息给特定客户端`);
                        ws.send(JSON.stringify({
                            type: 'interface_call_complete',
                            message: `接口调用成功`,
                            data: response.data,
                            requestId: response.requestId
                        }));
                    } else {
                        console.log('WebSocket不可用或未找到对应的WebSocket');
                    }
                    
                    // 清理映射
                    this.requestWsMap.delete(response.requestId);
                }
            } else if (response.requestId) {
                const handler = this.responseHandlers.get(response.requestId);
                const ws = this.requestWsMap.get(response.requestId);
                
                if (handler) {
                    this.responseHandlers.delete(response.requestId);
                    if (response.type === 'error') {
                        handler.reject(new Error(response.data?.message || '未知错误'));
                    } else {
                        handler.resolve(response.data);
                    }
                }
                
                // 清理映射
                this.requestWsMap.delete(response.requestId);
            }
        } catch (error) {
            console.error('❌ JSON解析失败:', error.message);
            console.error('失败消息长度:', message.length);
            console.error('失败消息开头200字符:', message.substring(0, 200));
        }
    }

    setWSBroadcastHandler(handler) {
        this.broadcastToWS = handler;
    }
}

const linuxClient = new LinuxTCPClient();

const app = http.createServer((req, res) => {
    if (req.url === '/') {
        const htmlPath = path.join(__dirname, 'index.html');
        fs.readFile(htmlPath, (err, data) => {
            if (err) {
                res.writeHead(500);
                res.end('Error loading HTML file');
            } else {
                res.writeHead(200, { 'Content-Type': 'text/html' });
                res.end(data);
            }
        });
    } else {
        res.writeHead(404);
        res.end('Not found');
    }
});

const wsClients = new Set();
const WebSocket = require('ws');
const wss = new WebSocket.Server({ server: app });

wss.on('connection', function connection(ws) {
    console.log('WebSocket客户端连接');
    wsClients.add(ws);

    ws.send(JSON.stringify({
        type: 'status',
        linuxConnected: linuxClient.isConnected,
        linuxHost: linuxClient.host || '',
        linuxPort: linuxClient.port || ''
    }));

    ws.on('message', function message(data) {
        try {
            const message = JSON.parse(data);
            console.log('接收到WebSocket消息:', message);

            if (message.type === 'connect_linux') {
                const host = message.host || '192.168.1.100';
                const port = message.port || 8081;
                
                linuxClient.host = host;
                linuxClient.port = port;
                
                linuxClient.connect().then(() => {
                    ws.send(JSON.stringify({
                        type: 'status',
                        linuxConnected: true,
                        message: '已连接到Linux服务端'
                    }));
                }).catch(error => {
                    ws.send(JSON.stringify({
                        type: 'error',
                        message: error.message
                    }));
                });
            } else if (message.type === 'interface_call') {
                if (!linuxClient.isConnected) {
                    ws.send(JSON.stringify({
                        type: 'error',
                        message: '未连接到Linux服务端'
                    }));
                    return;
                }

                const functionName = message.function_name || 'stDataLakeDef_interface';
                
                // 修改:传递当前WebSocket实例
                linuxClient.sendInterfaceCall(functionName, message.variables || {}, ws).then(() => {
                    ws.send(JSON.stringify({
                        type: 'interface_call_sent',
                        message: '接口调用指令已发送到Linux服务端',
                        function_name: functionName
                    }));
                }).catch(error => {
                    ws.send(JSON.stringify({
                        type: 'error',
                        message: error.message
                    }));
                });
            } else if (message.type === 'disconnect_linux') {
                linuxClient.disconnect();
                ws.send(JSON.stringify({
                    type: 'status',
                    linuxConnected: false,
                    message: '已断开与Linux服务端的连接'
                }));
            }

        } catch (error) {
            console.error('处理WebSocket消息失败:', error);
            ws.send(JSON.stringify({
                type: 'error',
                message: `消息处理失败: ${error.message}`
            }));
        }
    });

    ws.on('close', function() {
        console.log('WebSocket客户端断开连接');
        wsClients.delete(ws);
    });
});

linuxClient.setWSBroadcastHandler((message) => {
    const messageStr = JSON.stringify(message);
    console.log('广播消息给WebSocket客户端,类型:', message.type);
    wsClients.forEach((ws) => {
        if (ws.readyState === ws.OPEN) {
            ws.send(messageStr);
        }
    });
});

const HTTP_PORT = 3000;
app.listen(HTTP_PORT, '0.0.0.0', () => {
    console.log('=========================================');
    console.log('Windows客户端已启动!');
    console.log(`HTTP服务器: http://localhost:${HTTP_PORT}`);
    console.log('WebSocket服务器: ws://localhost:3000');
    console.log('=========================================');
});

process.on('SIGINT', function() {
    console.log('\n正在关闭服务器...');
    linuxClient.disconnect();
    process.exit(0);
});

package.json

javascript 复制代码
{
  "name": "tcp-websocket-server",
  "version": "1.0.0",
  "description": "TCP to WebSocket bridge server",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "dev": "node server.js"
  },
  "dependencies": {
    "ws": "^8.18.3"
  },
  "keywords": [
    "tcp",
    "websocket",
    "bridge"
  ],
  "author": "Your Name",
  "license": "MIT"
}

test.cpp

cpp 复制代码
#include "pub.h"

char* stWturInterface_interface();

extern "C" {
    extern INT16 WturCtrlHandler(INT16* pInput, INT16* pOutput, INT16* piInputSizeofByte, INT16* iOutpSizeofByte);
}

// 构建JSON响应 - 新增requestId参数
char* build_json_response(const char* type, const char* data, const char* message, const char* requestId, const char* function_name) {
    static char buffer[BUFFER_SIZE];
    
    if (strcmp(type, "error") == 0 && message) {
        // 对于错误类型
        if (requestId && requestId[0] != '\0') {
            snprintf(buffer, sizeof(buffer), 
                    "{\"type\":\"error\",\"message\":\"%s\",\"requestId\":\"%s\"}", message, requestId);
        } else {
            snprintf(buffer, sizeof(buffer), 
                    "{\"type\":\"error\",\"message\":\"%s\"}", message);
        }
    } else if (data) {
        // 接口响应类型
        if (requestId && requestId[0] != '\0') {
            if (function_name && function_name[0] != '\0') {
                snprintf(buffer, sizeof(buffer), 
                        "{\"type\":\"%s\",\"data\":{%s},\"requestId\":\"%s\",\"function_name\":\"%s\"}", 
                        type, data, requestId, function_name);
            } else {
                snprintf(buffer, sizeof(buffer), 
                        "{\"type\":\"%s\",\"data\":{%s},\"requestId\":\"%s\"}", 
                        type, data, requestId);
            }
        } else {
            snprintf(buffer, sizeof(buffer), 
                    "{\"type\":\"%s\",\"data\":{%s}}", type, data);
        }
    } else {
        snprintf(buffer, sizeof(buffer), 
                "{\"type\":\"%s\"}", type);
    }
    return buffer;
}

// 模拟的结构体接口函数 - 支持嵌套结构体
char* stDataLakeDef_interface(const char* parameters) {
    static char result[BUFFER_SIZE];
    char temp[BUFFER_SIZE] = {0};
    
    // 构建模拟的返回数据 - 按照头文件中的嵌套结构体
    // 顶级变量
    snprintf(temp, sizeof(temp),
        "\"WEB_bPowerProductionEnable\":\"TRUE\","
        "\"WEB_bTestModeEnable\":\"FALSE\","
        "\"WTUR_bSimuEqualDefaultValue21366\":\"TRUE\","
        "\"WTUR_bDataFileReadOk\":\"TRUE\","
        "\"WTUR_iGlobalStopLevel\":\"5\","
        "\"WFC_bArbitrationFlag\":\"FALSE\","
        "\"WFC_rWFCAutoRunUpRotorSpeedControlValue\":\"1250.75\","
        "\"PITCH_rPit1CapacitorVoltageU1_Inst_v\":\"325.8\","
        "\"PITCH_rPit2CapacitorVoltageU1_Inst_v\":\"324.2\","
        "\"PITCH_rPit3CapacitorVoltageU1_Inst_v\":\"326.1\","
        "\"WTUR_bRotorStopOk\":\"TRUE\","
        
        // module1 (stMoudleDefA) 的成员
        "\"module1.GL_Test_A\":\"TRUE\","
        "\"module1.GL_Test_B\":\"100\","
        "\"module1.GL_Test_C\":\"3.14\","
        
        // module1.module2 (stMoudleDefB) 的成员
        "\"module1.module2.WEB_Test_A\":\"FALSE\","
        "\"module1.module2.WEB_Test_B\":\"200\","
        "\"module1.module2.WEB_Test_C\":\"6.28\","
        
        // module1.module2.module3 (stMoudleDefC) 的成员
        "\"module1.module2.module3.THR_Test_A\":\"TRUE\","
        "\"module1.module2.module3.THR_Test_B\":\"300\","
        "\"module1.module2.module3.THR_Test_C\":\"9.42\","
        
        // module1.module2.module3.module4 (stMoudleDefD) 的成员
        "\"module1.module2.module3.module4.FOU_Test_A\":\"FALSE\","
        "\"module1.module2.module3.module4.FOU_Test_B\":\"400\","
        "\"module1.module2.module3.module4.FOU_Test_C\":\"12.56\"");
    
    // 复制到结果缓冲区
    strncpy(result, temp, sizeof(result) - 1);
    result[sizeof(result) - 1] = '\0';
    
    printf("接口调用参数: %s\n", parameters ? parameters : "无");
    printf("返回数据: %s\n", result);
    
    return result;
}

// 另一个示例函数
char* get_system_status(const char* parameters) {
    static char result[BUFFER_SIZE];
    char temp[BUFFER_SIZE] = {0};
    
    snprintf(temp, sizeof(temp),
        "\"cpu_usage\":\"45%%\","
        "\"memory_usage\":\"67%%\","
        "\"disk_usage\":\"23%%\","
        "\"network_status\":\"connected\","
        "\"uptime\":\"1234567\"");
    
    strncpy(result, temp, sizeof(result) - 1);
    result[sizeof(result) - 1] = '\0';
    
    printf("系统状态查询参数: %s\n", parameters ? parameters : "无");
    printf("返回系统状态数据: %s\n", result);
    
    return result;
}

// 网络信息函数
char* get_network_info(const char* parameters) {
    static char result[BUFFER_SIZE];
    char temp[BUFFER_SIZE] = {0};
    
    snprintf(temp, sizeof(temp),
        "\"ip_address\":\"192.168.1.100\","
        "\"subnet_mask\":\"255.255.255.0\","
        "\"gateway\":\"192.168.1.1\","
        "\"dns_server\":\"8.8.8.8\","
        "\"mac_address\":\"00:1B:44:11:3A:B7\"");
    
    strncpy(result, temp, sizeof(result) - 1);
    result[sizeof(result) - 1] = '\0';
    
    printf("网络信息查询参数: %s\n", parameters ? parameters : "无");
    printf("返回网络信息数据: %s\n", result);
    
    return result;
}

// 健壮的JSON解析函数 - 新增requestId解析
int parse_json_message(const char* json_str, char* type, size_t type_len, char* function, size_t func_len, char* requestId, size_t requestId_len) {
    // 初始化输出
    type[0] = '\0';
    function[0] = '\0';
    requestId[0] = '\0';
    
    printf("开始解析JSON: %s\n", json_str);
    
    const char* ptr = json_str;
    
    // 遍历JSON字符串
    while (*ptr) {
        // 查找键
        if (*ptr == '\"') {
            ptr++; // 跳过开头的引号
            
            // 提取键名
            char key[64] = {0};
            const char* key_end = strchr(ptr, '\"');
            if (!key_end) break;
            
            size_t key_len = key_end - ptr;
            if (key_len >= sizeof(key)) key_len = sizeof(key) - 1;
            strncpy(key, ptr, key_len);
            key[key_len] = '\0';
            
            ptr = key_end + 1; // 移动到键名后面
            
            // 查找冒号
            while (*ptr && *ptr != ':') ptr++;
            if (!*ptr) break;
            ptr++; // 跳过冒号
            
            // 跳过空格
            while (*ptr && *ptr == ' ') ptr++;
            if (!*ptr) break;
            
            // 处理值
            if (*ptr == '\"') {
                ptr++; // 跳过引号
                
                const char* value_end = strchr(ptr, '\"');
                if (!value_end) break;
                
                size_t value_len = value_end - ptr;
                
                // 根据键名存储到相应的变量
                if (strcmp(key, "type") == 0) {
                    if (value_len < type_len) {
                        strncpy(type, ptr, value_len);
                        type[value_len] = '\0';
                        printf("找到type: %s\n", type);
                    }
                } else if (strcmp(key, "function_name") == 0 || 
                          strcmp(key, "function-name") == 0 ||
                          strcmp(key, "functionName") == 0) {
                    if (value_len < func_len) {
                        strncpy(function, ptr, value_len);
                        function[value_len] = '\0';
                        printf("找到函数名: %s\n", function);
                    }
                } else if (strcmp(key, "requestId") == 0) {
                    if (value_len < requestId_len) {
                        strncpy(requestId, ptr, value_len);
                        requestId[value_len] = '\0';
                        printf("找到requestId: %s\n", requestId);
                    }
                }
                
                ptr = value_end + 1; // 移动到值后面
            } else {
                // 非字符串值,跳过
                while (*ptr && *ptr != ',' && *ptr != '}') ptr++;
            }
        } else {
            ptr++;
        }
    }
    
    printf("解析完成 - type: %s, function: %s, requestId: %s\n", type, function, requestId);
    return 0;
}

// 处理客户端连接
void* handle_client(void* arg) {
    int client_socket = *(int*)arg;
    free(arg);
    
    printf("新的客户端连接,socket: %d\n", client_socket);
    
    char buffer[BUFFER_SIZE];
    
    while (1) {
        int n = recv(client_socket, buffer, sizeof(buffer)-1, 0);
        
        if (n > 0) {
            buffer[n] = '\0';
            printf("接收到消息 (%d bytes): %s\n", n, buffer);
            
            // 解析消息
            char message_type[64];
            char function_name[64] = {0};
            char requestId[64] = {0};
            
            if (parse_json_message(buffer, message_type, sizeof(message_type), function_name, sizeof(function_name), requestId, sizeof(requestId)) == 0) {
                printf("消息类型: %s, 函数: %s, requestId: %s\n", 
                       message_type, function_name[0] ? function_name : "未指定", 
                       requestId[0] ? requestId : "未指定");
                
                if (strcmp(message_type, "interface_call") == 0) {
                    char* result_data = NULL;
                    
                    // 根据函数名调用对应的函数
                    if (strcmp(function_name, "stDataLakeDef_interface") == 0) {
                        printf("调用函数: stDataLakeDef_interface\n");
                        result_data = stDataLakeDef_interface(buffer);

                    } else if (strcmp(function_name, "WturCtrlHandler") == 0) {
                        INT16 pInput= 0, pOutput = 0;
                        INT16  piInputSizeofByte=0, iOutpSizeofByte=0;
                        printf("调用函数: WturCtrlHandler\n");
                        INT16 result_code = WturCtrlHandler(&pInput, &pOutput, &piInputSizeofByte, &iOutpSizeofByte);
                        printf("result_code=%d\n", result_code);
                        result_data = stWturInterface_interface();

                    } else {
                        printf("未知函数: %s\n", function_name);
                        // 使用新的错误格式,包含requestId
                        char* response = build_json_response("error", NULL, "未知函数", requestId, function_name);
                        printf("发送错误响应: %s\n", response);
                        send(client_socket, response, strlen(response), 0);
                        continue;
                    }
                    
                    // 调用接口函数并发送响应
                    if (result_data) {
                        // 使用新的响应格式,包含requestId和function_name
                        char* response = build_json_response("interface_response", result_data, NULL, requestId, function_name);
                        printf("发送响应: %s\n", response);
                        printf("报文长度: %lu\n", strlen(response));
                        send(client_socket, response, strlen(response), 0);
                        printf("接口响应发送成功\n");
                    }
                } else {
                    printf("未知消息类型: %s\n", message_type);
                    char* response = build_json_response("error", NULL, "未知消息类型", requestId, function_name);
                    send(client_socket, response, strlen(response), 0);
                }
            } else {
                printf("JSON解析失败\n");
                char* response = build_json_response("error", NULL, "JSON解析失败", "", "");
                send(client_socket, response, strlen(response), 0);
            }
        } else if (n == 0) {
            printf("客户端断开连接,socket: %d\n", client_socket);
            break;
        } else {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                // 超时,继续等待
                continue;
            } else {
                perror("接收数据失败");
                break;
            }
        }
    }
    
    close(client_socket);
    return NULL;
}

int main() {
    int server_socket, client_socket;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);
    
    // 创建socket
    server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket < 0) {
        perror("socket创建失败");
        exit(EXIT_FAILURE);
    }
    
    // 设置SO_REUSEADDR选项
    int reuse = 1;
    if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
        perror("setsockopt失败");
        close(server_socket);
        exit(EXIT_FAILURE);
    }
    
    // 配置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(SERVER_PORT);
    
    // 绑定端口
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("绑定端口失败");
        close(server_socket);
        exit(EXIT_FAILURE);
    }
    
    // 监听连接
    if (listen(server_socket, 5) < 0) {
        perror("监听失败");
        close(server_socket);
        exit(EXIT_FAILURE);
    }
    
    printf("=========================================\n");
    printf("Linux TCP服务端已启动!\n");
    printf("监听端口: %d\n", SERVER_PORT);
    printf("支持以下函数调用:\n");
    printf("- stDataLakeDef_interface\n");
    printf("- WturCtrlHandler\n");
    printf("等待Windows客户端连接...\n");
    printf("=========================================\n");
    
    // 接受客户端连接
    while (1) {
        client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len);
        if (client_socket < 0) {
            perror("接受连接失败");
            continue;
        }
        
        printf("新的客户端连接 from %s:%d\n", 
               inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
        
        // 为每个客户端创建新线程
        int* client_sock_ptr = (int*)malloc(sizeof(int));
        *client_sock_ptr = client_socket;
        
        pthread_t client_thread;
        if (pthread_create(&client_thread, NULL, handle_client, client_sock_ptr) != 0) {
            perror("创建线程失败");
            close(client_socket);
            free(client_sock_ptr);
        } else {
            pthread_detach(client_thread); // 分离线程,自动回收资源
        }
    }
    
    close(server_socket);
    printf("服务端已退出\n");
    return 0;
}

makefile

cpp 复制代码
# 编译器设置
CXX := aarch64-none-linux-gnu-g++

# 编译选项
CXXFLAGS := -Wall -O2 -I./include
LDFLAGS := -lm -g -lpthread  -L./lib -lwtur

# 目标文件
TARGET := test_tool
SRCS := test_tool.cpp wtur.cpp
OBJS := $(SRCS:.cpp=.o)

# 检测操作系统
ifeq ($(OS),Windows_NT)
    RM := del /Q
    RM_FILES := $(subst /,\,$(OBJS) $(TARGET))
else
    RM := rm -f
    RM_FILES := $(OBJS) $(TARGET)
endif

# 默认目标
all: $(TARGET)

# 编译目标
$(TARGET): $(OBJS)
	$(CXX) -o $@ $^ $(LDFLAGS)

# 生成对象文件
%.o: %.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@

# 清理 - 跨平台版本
clean:
	$(RM) $(RM_FILES)

.PHONY: all clean
相关推荐
爱吃巧克力的程序媛1 小时前
将qt界面中加载css或者qss样式
开发语言·css·qt
汪汪队立大功1232 小时前
JavaScript是怎么和html元素关联起来的?
开发语言·javascript·html
GISer_Jing5 小时前
Node.js 开发实战:从入门到精通
javascript·后端·node.js
5335ld5 小时前
后端给的post 方法但是要求传表单数据格式(没有{})
开发语言·前端·javascript·vue.js·ecmascript
QDKuz6 小时前
掌握Vue2转Vue3, Options API 转 Composition API
前端·javascript·vue.js
老前端的功夫6 小时前
前端Echarts性能优化:从卡顿到流畅的百万级数据可视化
前端·javascript
进击的野人6 小时前
深入解析localStorage:前端数据持久化的核心技术
前端·javascript
Mh6 小时前
如何优雅的消除“if...else...”
前端·javascript
码银6 小时前
docsify 本地部署完整配置模板 || 将md文件放到网页上展示
html·docsify·md