

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