-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`;
@@ -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 = `