From ba9b024cd9069b96a140c7a771983513a244e484 Mon Sep 17 00:00:00 2001 From: jinchao <383321154@qq.com> Date: Fri, 20 Jun 2025 16:57:50 +0800 Subject: [PATCH] =?UTF-8?q?V0.32.2.250620=5Falpha:=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=BC=80=E5=8F=91=E9=A1=B5=E9=9D=A2=E6=B7=BB=E5=8A=A0=E4=BA=86?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E4=BD=93=E7=9A=84=E5=AF=B9=E5=BA=94=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Release/database/XNSim.db | Bin 1224704 -> 1224704 bytes XNSimPortal/components/interface-config.js | 2 +- XNSimPortal/components/model-development.js | 289 ++++++++++++++++++-- XNSimPortal/routes/filesystem.js | 184 ++++++++++++- XNSimPortal/routes/interface-config.js | 4 +- XNSimPortal/utils/data-interface-utils.js | 4 +- 6 files changed, 452 insertions(+), 31 deletions(-) diff --git a/Release/database/XNSim.db b/Release/database/XNSim.db index d91ed912a19c3b1829aa0ec0f648ae758d21f209..5b2a020afbdbc3176e990c3751ce44aed8d79b8f 100644 GIT binary patch delta 704 zcmZp8;MMTJYl1Z6pNTTgjDI#Jtf=SU{lK+>XDyG#Wg&n!M!uK$5pwNiv%aAvNR6-e01 z$}z;zz{JXGx}h$Uu$_{XQgKO9X>y4|W?n&QNql)~W_m_Re6SV@U#S*{8HvhFB5?>a z@=Hrl4M7pWWlA{MPN&42#JuFx_>9!VqLQT4#1f#fnRz7&)1y3?q*xS6lxmy*^|$}) zXWag;pNUODf{VqJfuEbtns*w{J?`J!{alaO>R2OLOo0I<$x`2>7{n>5C@x#vEG%xI zXKbWrWMp7us%v1TYh;j^Q;?BpXl7+NwE7NK#=0KkQ>Fghvdbdye!1RuLVgPS@ w{~snGW(HywAZ7((HXvpPVh$kY1Y#~A<_2OOAO@-D17dz47TDhZN3d`+0P{3b_y7O^ diff --git a/XNSimPortal/components/interface-config.js b/XNSimPortal/components/interface-config.js index 0401a07..60c27d7 100644 --- a/XNSimPortal/components/interface-config.js +++ b/XNSimPortal/components/interface-config.js @@ -310,7 +310,7 @@ class InterfaceConfig extends HTMLElement { throw new Error('请先选择构型'); } - const response = await fetch(`/api/interface/list?systemName=XNSim&confID=${selection.configurationId}`); + const response = await fetch(`/api/interface/list?confID=${selection.configurationId}`); if (!response.ok) { throw new Error('获取数据失败'); } diff --git a/XNSimPortal/components/model-development.js b/XNSimPortal/components/model-development.js index ab09fb6..c131e72 100644 --- a/XNSimPortal/components/model-development.js +++ b/XNSimPortal/components/model-development.js @@ -962,25 +962,56 @@ class ModelDevelopment extends HTMLElement { // 创建结构体参数标题 const structHeader = document.createElement('div'); structHeader.style.cssText = 'margin-bottom: 15px;'; - structHeader.innerHTML = ''; + structHeader.innerHTML = ''; // 创建结构体参数网格布局 const structGrid = document.createElement('div'); - structGrid.style.cssText = 'display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;'; + structGrid.style.cssText = 'display: grid; grid-template-columns: auto repeat(3, 1fr); gap: 20px;'; - // 添加三个结构体输入框 + // 添加六个下拉框,按类型分组 structGrid.innerHTML = ` -
- - +
+
数据库中:
+
头文件中:
-
- - +
+
输入
+
+ +
+
+ +
-
- - +
+
输出
+
+ +
+
+ +
+
+
+
心跳
+
+ +
+
+ +
`; @@ -1025,8 +1056,8 @@ class ModelDevelopment extends HTMLElement { // 组装表单 form.appendChild(basicInfoSection); form.appendChild(advancedSection); - form.appendChild(cmdListSection); form.appendChild(structSection); + form.appendChild(cmdListSection); form.appendChild(formActions); formContainer.appendChild(form); container.appendChild(formContainer); @@ -1071,6 +1102,9 @@ class ModelDevelopment extends HTMLElement { this.updateRunNodeOptions(); }); } + + // 填充数据库中结构体下拉框 + this.populateDatabaseStructDropdowns(); }, 0); // 添加表单提交事件 @@ -1151,6 +1185,17 @@ class ModelDevelopment extends HTMLElement { } }); + // 构建结构体映射JSON字符串 + const buildStructMapping = (select1, select2) => { + const selectedOption1 = select1.selectedOptions[0]; + const value2 = select2.value; + + if (selectedOption1 && selectedOption1.dataset.fullname && value2) { + return JSON.stringify({ [selectedOption1.dataset.fullname]: value2 }); + } + return ''; + }; + // 构建版本数据 const versionData = { PlaneName: selection.plane, @@ -1169,9 +1214,18 @@ class ModelDevelopment extends HTMLElement { DataPackageHeaderName: form.querySelector('#dataPackageHeaderName').value, DataPackageEntryPoint: form.querySelector('#dataPackageEntryPoint').value, DataPackageInterfaceName: form.querySelector('#dataPackageInterfaceName').value, - InputStruct: form.querySelector('#inputStruct').value, - OutputStruct: form.querySelector('#outputStruct').value, - HeartStruct: form.querySelector('#heartStruct').value, + InputStruct: buildStructMapping( + form.querySelector('#inputStructMapping1'), + form.querySelector('#inputStructMapping2') + ), + OutputStruct: buildStructMapping( + form.querySelector('#outputStructMapping1'), + form.querySelector('#outputStructMapping2') + ), + HeartStruct: buildStructMapping( + form.querySelector('#heartStructMapping1'), + form.querySelector('#heartStructMapping2') + ), CmdList: JSON.stringify(cmdList), isUpdate: this.isEditMode, originalVersion: this.isEditMode ? this.currentVersion.Version : null @@ -2223,6 +2277,197 @@ class ModelDevelopment extends HTMLElement { } } + /** + * 填充数据库中结构体下拉框 + */ + async populateDatabaseStructDropdowns() { + try { + const savedSelection = localStorage.getItem('xnsim-selection'); + const selection = savedSelection ? JSON.parse(savedSelection) : {}; + + if (!selection.configurationId) { + console.warn('未找到构型ID,无法获取结构体列表'); + return; + } + + const structResponse = await fetch(`/api/interface/list?confID=${selection.configurationId}`); + if (structResponse.ok) { + const structData = await structResponse.json(); + + // 以ModelStructName为key去重,只保留第一个全称 + const uniqueMap = {}; + structData.forEach(item => { + if (!uniqueMap[item.ModelStructName]) { + uniqueMap[item.ModelStructName] = `${item.SystemName}::${item.PlaneName}::${item.ATAName}::${item.ModelStructName}`; + } + }); + const structNames = Object.keys(uniqueMap); + + // 获取下拉框元素 + const inputStructMapping1 = this.shadowRoot.querySelector('#inputStructMapping1'); + const outputStructMapping1 = this.shadowRoot.querySelector('#outputStructMapping1'); + const heartStructMapping1 = this.shadowRoot.querySelector('#heartStructMapping1'); + + // 填充数据库中结构体下拉框 + if (inputStructMapping1) { + inputStructMapping1.innerHTML = '' + + structNames.map(name => ``).join(''); + } + if (outputStructMapping1) { + outputStructMapping1.innerHTML = '' + + structNames.map(name => ``).join(''); + } + if (heartStructMapping1) { + heartStructMapping1.innerHTML = '' + + structNames.map(name => ``).join(''); + } + + // 解析数据库中存储的JSON字符串并自动选择正确的选项 + this.selectStructMappingOptions(); + + // 检查数据包相关字段是否有值,如果有则获取结构体成员信息 + const dataPackagePathInput = this.shadowRoot.querySelector('#dataPackagePath'); + const dataPackageHeaderNameInput = this.shadowRoot.querySelector('#dataPackageHeaderName'); + const dataPackageInterfaceNameInput = this.shadowRoot.querySelector('#dataPackageInterfaceName'); + if (dataPackagePathInput && dataPackageHeaderNameInput && dataPackageInterfaceNameInput) { + + const packagePath = dataPackagePathInput.value.trim(); + const hearderName = dataPackageHeaderNameInput.value.trim(); + const interfaceName = dataPackageInterfaceNameInput.value.trim(); + + if (packagePath && hearderName && interfaceName) { + const headerFilePath = packagePath + '/' + hearderName; + try { + const memberResponse = await fetch('/api/filesystem/get-struct-members', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + confName: selection.configurationName, + headerFilePath: headerFilePath, + structName: interfaceName + }) + }); + + if (memberResponse.ok) { + const memberData = await memberResponse.json(); + + if (memberData.success && memberData.memberNames) { + // 获取头文件结构体下拉框 + const inputStructMapping2 = this.shadowRoot.querySelector('#inputStructMapping2'); + const outputStructMapping2 = this.shadowRoot.querySelector('#outputStructMapping2'); + const heartStructMapping2 = this.shadowRoot.querySelector('#heartStructMapping2'); + + // 填充头文件中结构体下拉框 + if (inputStructMapping2 && outputStructMapping2 && heartStructMapping2) { + const memberOptions = '' + + memberData.memberNames.map(member => ``).join(''); + + inputStructMapping2.innerHTML = memberOptions; + outputStructMapping2.innerHTML = memberOptions; + heartStructMapping2.innerHTML = memberOptions; + + // 在填充头文件结构体下拉框后,再次解析JSON并选择选项 + this.selectStructMappingOptions(); + } + } + } else { + console.warn('获取结构体成员信息失败:', memberResponse.status); + } + } catch (error) { + console.warn('获取结构体成员信息失败:', error); + } + } + } + } else { + console.warn('获取接口结构体列表失败:', structResponse.status); + } + } catch (error) { + console.warn('填充数据库结构体下拉框失败:', error); + } + } + + /** + * 解析数据库中存储的JSON字符串并自动选择正确的选项 + */ + selectStructMappingOptions() { + if (!this.currentVersion) return; + + // 解析InputStruct + if (this.currentVersion.InputStruct) { + try { + const inputStructData = JSON.parse(this.currentVersion.InputStruct); + const inputStructKey = Object.keys(inputStructData)[0]; // 获取JSON对象的key + const inputStructValue = inputStructData[inputStructKey]; // 获取JSON对象的value + + // 选择数据库结构体下拉框 + const inputStructMapping1 = this.shadowRoot.querySelector('#inputStructMapping1'); + if (inputStructMapping1) { + // 从全称中提取结构体名 + const structName = inputStructKey.split('::').pop(); + inputStructMapping1.value = structName; + } + + // 选择头文件结构体下拉框 + const inputStructMapping2 = this.shadowRoot.querySelector('#inputStructMapping2'); + if (inputStructMapping2) { + inputStructMapping2.value = inputStructValue; + } + } catch (error) { + console.warn('解析InputStruct失败:', error); + } + } + + // 解析OutputStruct + if (this.currentVersion.OutputStruct) { + try { + const outputStructData = JSON.parse(this.currentVersion.OutputStruct); + const outputStructKey = Object.keys(outputStructData)[0]; + const outputStructValue = outputStructData[outputStructKey]; + + // 选择数据库结构体下拉框 + const outputStructMapping1 = this.shadowRoot.querySelector('#outputStructMapping1'); + if (outputStructMapping1) { + const structName = outputStructKey.split('::').pop(); + outputStructMapping1.value = structName; + } + + // 选择头文件结构体下拉框 + const outputStructMapping2 = this.shadowRoot.querySelector('#outputStructMapping2'); + if (outputStructMapping2) { + outputStructMapping2.value = outputStructValue; + } + } catch (error) { + console.warn('解析OutputStruct失败:', error); + } + } + + // 解析HeartStruct + if (this.currentVersion.HeartStruct) { + try { + const heartStructData = JSON.parse(this.currentVersion.HeartStruct); + const heartStructKey = Object.keys(heartStructData)[0]; + const heartStructValue = heartStructData[heartStructKey]; + + // 选择数据库结构体下拉框 + const heartStructMapping1 = this.shadowRoot.querySelector('#heartStructMapping1'); + if (heartStructMapping1) { + const structName = heartStructKey.split('::').pop(); + heartStructMapping1.value = structName; + } + + // 选择头文件结构体下拉框 + const heartStructMapping2 = this.shadowRoot.querySelector('#heartStructMapping2'); + if (heartStructMapping2) { + heartStructMapping2.value = heartStructValue; + } + } catch (error) { + console.warn('解析HeartStruct失败:', error); + } + } + } + /** * 上传数据包模型 */ @@ -2350,6 +2595,18 @@ class ModelDevelopment extends HTMLElement { dataPackageInterfaceNameInput.value = result.paramType; } + //将结构体名称填入对应下拉框的候选列表 + const inputStructMapping2 = this.shadowRoot.querySelector('#inputStructMapping2'); + const outputStructMapping2 = this.shadowRoot.querySelector('#outputStructMapping2'); + const heartStructMapping2 = this.shadowRoot.querySelector('#heartStructMapping2'); + + // 填充头文件中结构体下拉框 + if (inputStructMapping2 && outputStructMapping2 && heartStructMapping2) { + inputStructMapping2.innerHTML = '' + result.memberNames.map(name => ``).join(''); + outputStructMapping2.innerHTML = '' + result.memberNames.map(name => ``).join(''); + heartStructMapping2.innerHTML = '' + result.memberNames.map(name => ``).join(''); + } + alert(`数据包上传成功!\n数据包路径: ${result.packagePath}\n头文件: ${result.headerFile}\n动态库文件: ${result.libraryFile}\n入口点: ${result.entryPoint}\n参数类型: ${result.paramType}`); } else { throw new Error(result.message || '上传失败'); diff --git a/XNSimPortal/routes/filesystem.js b/XNSimPortal/routes/filesystem.js index c41b864..5c65b8b 100644 --- a/XNSimPortal/routes/filesystem.js +++ b/XNSimPortal/routes/filesystem.js @@ -488,14 +488,6 @@ router.get('/download', async (req, res) => { // 上传数据包文件夹 router.post('/upload-package', packageUpload.array('files'), async (req, res) => { try { - console.log('接收到的请求体:', req.body); - console.log('接收到的文件:', req.files ? req.files.map(f => f.originalname) : '无文件'); - console.log('文件详细信息:', req.files ? req.files.map(f => ({ - originalname: f.originalname, - webkitRelativePath: f.webkitRelativePath, - fieldname: f.fieldname - })) : '无文件'); - const { confName } = req.body; const { folderName } = req.body; // 从前端获取文件夹名称 @@ -697,6 +689,23 @@ router.post('/upload-package', packageUpload.array('files'), async (req, res) => // 不阻止上传流程,只记录警告 } + let memberNames = []; + // 如果获取到了入口点函数信息,尝试从头文件中查找对应的结构体定义 + if (entryPointInfo && entryPointInfo.paramType) { + try { + const headerFile = uploadedFiles.find(file => file.type === 'header'); + if (headerFile) { + const headerFilePath = path.join(targetPackagePath, headerFile.path); + const structMemberNames = await findStructDefinition(headerFilePath, entryPointInfo.paramType); + if (structMemberNames) { + memberNames = structMemberNames; + } + } + } catch (error) { + console.warn('查找结构体定义失败:', error.message); + } + } + // 返回上传结果 res.json({ success: true, @@ -705,7 +714,8 @@ router.post('/upload-package', packageUpload.array('files'), async (req, res) => headerFile: path.basename(fileTypes.headerFiles[0].originalname), libraryFile: path.basename(fileTypes.libraryFiles[0].originalname), entryPoint: entryPointInfo ? entryPointInfo.symbolName : null, - paramType: entryPointInfo ? entryPointInfo.paramType : null + paramType: entryPointInfo ? entryPointInfo.paramType : null, + memberNames: memberNames }); } catch (error) { @@ -765,7 +775,6 @@ async function getEntryPointInfo(libraryPath) { paramType: paramType }; - console.log('动态库入口点信息:', entryPoint); return entryPoint; } } @@ -778,4 +787,157 @@ async function getEntryPointInfo(libraryPath) { } } -module.exports = router; \ No newline at end of file +/** + * 查找指定结构体的定义并返回其成员信息 + * @param {string} headerFilePath - 头文件路径 + * @param {string} structName - 要查找的结构体名称 + * @returns {Promise>} 结构体成员信息数组,格式为"type memberName" + */ +async function findStructDefinition(headerFilePath, structName) { + try { + // 检查文件是否存在 + await fsPromises.access(headerFilePath); + + // 读取头文件内容 + const content = await fsPromises.readFile(headerFilePath, 'utf8'); + + // 移除注释,避免干扰解析 + const contentWithoutComments = content + // 移除单行注释 + .replace(/\/\/.*$/gm, '') + // 移除多行注释 + .replace(/\/\*[\s\S]*?\*\//g, ''); + + // 构建查找结构体定义的正则表达式 + // 匹配以下格式: + // struct StructName { + // struct StructName{ + // typedef struct StructName { + // typedef struct StructName{ + const structPattern = new RegExp( + `(?:typedef\\s+)?struct\\s+${structName}\\s*\\{([\\s\\S]*?)\\}\\s*;?`, + 'g' + ); + + const memberNames = []; + let match; + + while ((match = structPattern.exec(contentWithoutComments)) !== null) { + const structBody = match[1]; + // 按行分割结构体内容 + const lines = structBody.split('\n'); + + for (const line of lines) { + const trimmedLine = line.trim(); + + // 跳过空行和只包含分号的行 + if (!trimmedLine || trimmedLine === ';') { + continue; + } + + // 匹配成员声明 + // 基础类型: int, double, float, char, short, long, unsigned, etc. + // 结构体指针: struct xxx_S* + // 结构体: struct xxx_S + const memberRegex = /^(?:const\s+)?(?:struct\s+([A-Za-z_][A-Za-z0-9_]*)\s*)?(?:unsigned\s+)?(?:long\s+)?(?:int\s+)?(?:char\s+)?(?:short\s+)?(?:float\s+)?(?:double\s+)?(?:void\s*)?(?:[A-Za-z_][A-Za-z0-9_]*\s*)?(\*?)\s*([A-Za-z_][A-Za-z0-9_]*)\s*(?:\[[^\]]*\])?\s*;?\s*$/; + + const memberMatch = trimmedLine.match(memberRegex); + if (memberMatch) { + const structType = memberMatch[1]; // 结构体类型名 + const isPointer = memberMatch[2]; // 是否为指针 + const memberName = memberMatch[3]; // 成员名 + + // 构建类型信息 + let type = ''; + if (structType) { + // 结构体类型 + type = `struct ${structType}${isPointer ? '*' : ''}`; + } else { + // 基础类型,需要从原始行中提取 + const typeMatch = trimmedLine.match(/^(?:const\s+)?(?:unsigned\s+)?(?:long\s+)?(?:int\s+)?(?:char\s+)?(?:short\s+)?(?:float\s+)?(?:double\s+)?(?:void\s*)?(?:[A-Za-z_][A-Za-z0-9_]*\s*)?/); + if (typeMatch) { + type = typeMatch[0].trim() + (isPointer ? '*' : ''); + } else { + type = 'unknown'; + } + } + + // 拼接类型和成员名 + const memberString = `${type} ${memberName}`; + memberNames.push(memberString); + } + } + } + return memberNames; + + } catch (error) { + console.warn(`查找结构体 ${structName} 定义失败:`, error.message); + return []; + } +} + +/** + * 根据构型名、头文件路径和结构体名获取结构体成员信息 + */ +router.post('/get-struct-members', async (req, res) => { + try { + const { confName, headerFilePath, structName } = req.body; + + if (!confName) { + return res.status(400).json({ + success: false, + message: '未提供构型名称' + }); + } + + if (!headerFilePath) { + return res.status(400).json({ + success: false, + message: '未提供头文件路径' + }); + } + + if (!structName) { + return res.status(400).json({ + success: false, + message: '未提供结构体名称' + }); + } + + // 获取数据包路径 + const { getPackagesPath } = require('../utils/file-utils'); + const packagesPath = getPackagesPath(confName); + + if (!packagesPath) { + return res.status(400).json({ + success: false, + message: '无法获取数据包路径' + }); + } + + // 组装完整的头文件路径 + const fullHeaderFilePath = path.join(packagesPath, headerFilePath); + + // 调用findStructDefinition函数获取结构体成员信息 + const memberNames = await findStructDefinition(fullHeaderFilePath, structName); + + res.json({ + success: true, + confName: confName, + structName: structName, + headerFilePath: headerFilePath, + fullHeaderFilePath: fullHeaderFilePath, + memberNames: memberNames, + count: memberNames.length + }); + + } catch (error) { + console.error('获取结构体成员信息失败:', error); + res.status(500).json({ + success: false, + message: '获取结构体成员信息失败: ' + error.message + }); + } +}); + +module.exports = router; diff --git a/XNSimPortal/routes/interface-config.js b/XNSimPortal/routes/interface-config.js index 1630ea5..305ce4f 100644 --- a/XNSimPortal/routes/interface-config.js +++ b/XNSimPortal/routes/interface-config.js @@ -30,11 +30,11 @@ async function ensureDataDirectory() { // 获取接口列表 router.get('/list', async (req, res) => { try { - const { systemName, confID } = req.query; + const { confID } = req.query; if (!confID) { return res.status(400).json({ error: 'ConfID 是必填字段' }); } - const interfaces = await getDataInterfaces(systemName, confID); + const interfaces = await getDataInterfaces(confID); res.json(interfaces); } catch (error) { console.error('获取接口列表失败:', error); diff --git a/XNSimPortal/utils/data-interface-utils.js b/XNSimPortal/utils/data-interface-utils.js index 3e4caf1..b699045 100644 --- a/XNSimPortal/utils/data-interface-utils.js +++ b/XNSimPortal/utils/data-interface-utils.js @@ -1,7 +1,7 @@ const { getDBConnection } = require('./file-utils'); // 获取接口列表 -function getDataInterfaces(systemName = 'XNSim', confID) { +function getDataInterfaces(confID) { try { if (!confID) { throw new Error('ConfID 是必填字段'); @@ -10,6 +10,8 @@ function getDataInterfaces(systemName = 'XNSim', confID) { const db = getDBConnection(true); const tableName = `DataInterface_${confID}`; + + const systemName = 'XNSim' // 查询所有接口 const query = `