V0.32.1.250620_alpha:模型开发新增数据包模型上传功能

This commit is contained in:
jinchao 2025-06-20 14:52:49 +08:00
parent fa8f4f0234
commit 129e1459ca
8 changed files with 635 additions and 40 deletions

Binary file not shown.

View File

@ -11,7 +11,7 @@ std::string GenIDL::idlFilePath = "";
bool GenIDL::createConfigDirectory(const std::string &configName)
{
std::string dirPath = GetXNCoreEnv() + "/IDL/" + configName;
std::string dirPath = GetXNCoreEnv() + "/Configuration/" + configName + "/IDL";
try {
fs::create_directories(dirPath);
GenIDL::idlFilePath = dirPath + "/" + configName + ".idl";

View File

@ -21,11 +21,18 @@ class HeaderTools extends HTMLElement {
return selectedOption ? selectedOption.dataset.domainId : '';
}
get selectedConfigurationName() {
const select = this.shadowRoot.getElementById('configurationSelect');
const selectedOption = select.options[select.selectedIndex];
return selectedOption ? selectedOption.textContent : '';
}
// 保存选择到localStorage
saveSelection() {
const selection = {
plane: this.selectedPlane,
configurationId: this.selectedConfiguration,
configurationName: this.selectedConfigurationName,
domainId: this.selectedDomain
};
localStorage.setItem('xnsim-selection', JSON.stringify(selection));

View File

@ -782,10 +782,12 @@ class ModelDevelopment extends HTMLElement {
}
}
},
{ text: '生成代码', color: '#805ad5', action: () => alert('生成代码功能即将上线') },
{ text: '编辑代码', color: '#d69e2e', action: () => alert('编辑代码功能即将上线') },
{ text: '模型编译', color: '#dd6b20', action: () => alert('模型编译功能即将上线') },
{ text: '模型提交', color: '#e53e3e', action: () => alert('模型提交功能即将上线') }
{
text: '数据包模型上传',
color: '#dd6b20',
action: () => this.uploadDataPackage()
},
{ text: '自动封装', color: '#e53e3e', action: () => alert('自动封装功能即将上线') }
];
buttonConfigs.forEach(config => {
@ -878,17 +880,17 @@ class ModelDevelopment extends HTMLElement {
// 添加左列输入框
const leftAdvancedColumn = document.createElement('div');
leftAdvancedColumn.innerHTML = `
<div class="form-group">
<label for="codePath">代码路径 (CodePath)</label>
<input type="text" id="codePath" name="CodePath" value="${this.currentVersion.CodePath || ''}" title="模型代码文件的路径">
</div>
<div class="form-group">
<label for="dataPackagePath">数据包路径 (DataPackagePath)</label>
<input type="text" id="dataPackagePath" name="DataPackagePath" value="${this.currentVersion.DataPackagePath || ''}" title="数据包文件的路径">
</div>
<div class="form-group">
<label for="dataPackageHeaderPath">数据包头文件路径 (DataPackageHeaderPath)</label>
<input type="text" id="dataPackageHeaderPath" name="DataPackageHeaderPath" value="${this.currentVersion.DataPackageHeaderPath || ''}" title="数据包头文件的路径">
<label for="dataPackageName">数据包名称 (DataPackageName)</label>
<input type="text" id="dataPackageName" name="DataPackageName" value="${this.currentVersion.DataPackageName || ''}" title="数据包的名称">
</div>
<div class="form-group">
<label for="dataPackageHeaderName">数据包头文件名称 (DataPackageHeaderName)</label>
<input type="text" id="dataPackageHeaderName" name="DataPackageHeaderName" value="${this.currentVersion.DataPackageHeaderName || ''}" title="数据包头文件的名称">
</div>
`;
@ -952,6 +954,39 @@ class ModelDevelopment extends HTMLElement {
cmdListSection.className = 'form-section cmd-list';
cmdListSection.style.cssText = 'border-top: 1px solid #e2e8f0; padding-top: 20px; margin-top: 20px;';
// 添加结构体参数部分
const structSection = document.createElement('div');
structSection.className = 'form-section struct-params';
structSection.style.cssText = 'border-top: 1px solid #e2e8f0; padding-top: 20px; margin-top: 20px;';
// 创建结构体参数标题
const structHeader = document.createElement('div');
structHeader.style.cssText = 'margin-bottom: 15px;';
structHeader.innerHTML = '<label style="font-weight: 500; color: #333;">结构体参数</label>';
// 创建结构体参数网格布局
const structGrid = document.createElement('div');
structGrid.style.cssText = 'display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;';
// 添加三个结构体输入框
structGrid.innerHTML = `
<div class="form-group">
<label for="inputStruct">输入结构体 (InputStruct)</label>
<textarea id="inputStruct" name="InputStruct" placeholder="输入结构体JSON格式" title="模型的输入参数结构体JSON格式" style="min-height: 80px; resize: vertical;">${this.currentVersion.InputStruct || ''}</textarea>
</div>
<div class="form-group">
<label for="outputStruct">输出结构体 (OutputStruct)</label>
<textarea id="outputStruct" name="OutputStruct" placeholder="输出结构体JSON格式" title="模型的输出参数结构体JSON格式" style="min-height: 80px; resize: vertical;">${this.currentVersion.OutputStruct || ''}</textarea>
</div>
<div class="form-group">
<label for="heartStruct">心跳结构体 (HeartStruct)</label>
<textarea id="heartStruct" name="HeartStruct" placeholder="心跳结构体JSON格式" title="模型的心跳参数结构体JSON格式" style="min-height: 80px; resize: vertical;">${this.currentVersion.HeartStruct || ''}</textarea>
</div>
`;
structSection.appendChild(structHeader);
structSection.appendChild(structGrid);
// 创建表格标题和工具栏
const cmdListHeader = document.createElement('div');
cmdListHeader.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;';
@ -991,6 +1026,7 @@ class ModelDevelopment extends HTMLElement {
form.appendChild(basicInfoSection);
form.appendChild(advancedSection);
form.appendChild(cmdListSection);
form.appendChild(structSection);
form.appendChild(formActions);
formContainer.appendChild(form);
container.appendChild(formContainer);
@ -1068,6 +1104,34 @@ class ModelDevelopment extends HTMLElement {
}
}
// 验证结构体字段的JSON格式
const inputStructInput = form.querySelector('#inputStruct');
if (inputStructInput && inputStructInput.value.trim() !== '') {
try {
JSON.parse(inputStructInput.value);
} catch (e) {
throw new Error('InputStruct 必须是有效的 JSON 格式');
}
}
const outputStructInput = form.querySelector('#outputStruct');
if (outputStructInput && outputStructInput.value.trim() !== '') {
try {
JSON.parse(outputStructInput.value);
} catch (e) {
throw new Error('OutputStruct 必须是有效的 JSON 格式');
}
}
const heartStructInput = form.querySelector('#heartStruct');
if (heartStructInput && heartStructInput.value.trim() !== '') {
try {
JSON.parse(heartStructInput.value);
} catch (e) {
throw new Error('HeartStruct 必须是有效的 JSON 格式');
}
}
// 收集指令列表数据
const cmdList = [];
const cmdRows = this.shadowRoot.querySelectorAll('#cmdListBody tr');
@ -1097,14 +1161,17 @@ class ModelDevelopment extends HTMLElement {
Author: form.querySelector('#author').value,
Description: form.querySelector('#description').value,
ChangeTime: form.querySelector('#changeTime').value,
CodePath: form.querySelector('#codePath').value,
RunFreqGroup: form.querySelector('#runFreqGroup').value,
RunNode: form.querySelector('#runNode').value,
Priority: form.querySelector('#priority').value,
DataPackagePath: form.querySelector('#dataPackagePath').value,
DataPackageHeaderPath: form.querySelector('#dataPackageHeaderPath').value,
DataPackageName: form.querySelector('#dataPackageName').value,
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,
CmdList: JSON.stringify(cmdList),
isUpdate: this.isEditMode,
originalVersion: this.isEditMode ? this.currentVersion.Version : null
@ -1940,14 +2007,17 @@ class ModelDevelopment extends HTMLElement {
Description: '',
CreatTime: this.getCurrentDateTime(),
ChangeTime: this.getCurrentDateTime(),
CodePath: '',
RunFreqGroup: '0',
RunNode: '0',
Priority: '0',
DataPackagePath: '',
DataPackageHeaderPath: '',
DataPackageName: '',
DataPackageHeaderName: '',
DataPackageEntryPoint: '',
DataPackageInterfaceName: '',
InputStruct: '',
OutputStruct: '',
HeartStruct: '',
CmdList: [] // 初始化为空数组
};
@ -2152,6 +2222,163 @@ class ModelDevelopment extends HTMLElement {
}
}
}
/**
* 上传数据包模型
*/
async uploadDataPackage() {
try {
// 获取当前选择的构型信息
const savedSelection = localStorage.getItem('xnsim-selection');
const selection = savedSelection ? JSON.parse(savedSelection) : {};
if (!selection.configurationName) {
alert('请先选择构型!');
return;
}
// 创建文件输入元素
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.webkitdirectory = true;
fileInput.directory = true;
fileInput.multiple = true;
fileInput.style.display = 'none';
// 添加到DOM
document.body.appendChild(fileInput);
// 监听文件选择
fileInput.addEventListener('change', async (event) => {
try {
const files = Array.from(event.target.files);
if (files.length === 0) {
alert('请选择文件夹!');
return;
}
// 从第一个文件的webkitRelativePath中获取文件夹名称
let folderName = null;
if (files.length > 0 && files[0].webkitRelativePath) {
const pathParts = files[0].webkitRelativePath.split('/');
if (pathParts.length > 1) {
folderName = pathParts[0];
}
}
if (!folderName) {
alert('无法获取文件夹名称,请重新选择文件夹!');
return;
}
// 验证文件夹内容
const headerFiles = files.filter(file => file.name.toLowerCase().endsWith('.h'));
const libraryFiles = files.filter(file => file.name.toLowerCase().includes('.so'));
if (headerFiles.length !== 1) {
alert(`文件夹必须包含且仅包含一个.h文件当前包含 ${headerFiles.length} 个.h文件`);
return;
}
if (libraryFiles.length !== 1) {
alert(`文件夹必须包含且仅包含一个动态库文件,当前包含 ${libraryFiles.length} 个动态库文件`);
return;
}
if (files.length !== 2) {
alert(`文件夹只能包含一个.h文件和一个动态库文件当前包含 ${files.length} 个文件`);
return;
}
// 创建FormData
const formData = new FormData();
formData.append('confName', selection.configurationName);
formData.append('folderName', folderName);
// 添加所有文件
files.forEach(file => {
formData.append('files', file);
});
// 显示上传进度
const uploadButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(3)');
if (uploadButton) {
const originalText = uploadButton.textContent;
uploadButton.textContent = '上传中...';
uploadButton.disabled = true;
}
// 发送上传请求
const response = await fetch('/api/filesystem/upload-package', {
method: 'POST',
body: formData
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || `上传失败: ${response.status}`);
}
const result = await response.json();
if (result.success) {
// 将返回的值填入对应的输入框
const dataPackagePathInput = this.shadowRoot.querySelector('#dataPackagePath');
const dataPackageNameInput = this.shadowRoot.querySelector('#dataPackageName');
const dataPackageHeaderNameInput = this.shadowRoot.querySelector('#dataPackageHeaderName');
const dataPackageEntryPointInput = this.shadowRoot.querySelector('#dataPackageEntryPoint');
const dataPackageInterfaceNameInput = this.shadowRoot.querySelector('#dataPackageInterfaceName');
if (dataPackagePathInput) {
dataPackagePathInput.value = result.packagePath;
}
if (dataPackageNameInput) {
dataPackageNameInput.value = result.libraryFile;
}
if (dataPackageHeaderNameInput) {
dataPackageHeaderNameInput.value = result.headerFile;
}
if (dataPackageEntryPointInput) {
dataPackageEntryPointInput.value = result.entryPoint;
}
if (dataPackageInterfaceNameInput) {
dataPackageInterfaceNameInput.value = result.paramType;
}
alert(`数据包上传成功!\n数据包路径: ${result.packagePath}\n头文件: ${result.headerFile}\n动态库文件: ${result.libraryFile}\n入口点: ${result.entryPoint}\n参数类型: ${result.paramType}`);
} else {
throw new Error(result.message || '上传失败');
}
} catch (error) {
console.error('数据包上传失败:', error);
alert(`数据包上传失败: ${error.message}`);
} finally {
// 恢复按钮状态
const uploadButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(3)');
if (uploadButton) {
uploadButton.textContent = '数据包模型上传';
uploadButton.disabled = false;
}
// 清理文件输入元素
document.body.removeChild(fileInput);
}
});
// 触发文件选择对话框
fileInput.click();
} catch (error) {
console.error('数据包上传初始化失败:', error);
alert(`数据包上传失败: ${error.message}`);
}
}
}
customElements.define('model-development', ModelDevelopment);

View File

@ -4,8 +4,12 @@ const fsPromises = require('fs').promises;
const fs = require('fs');
const path = require('path');
const multer = require('multer');
const { exec } = require('child_process');
const { promisify } = require('util');
const { getActualLogPath, getUploadPath, saveUploadedFile, isAllowedFileType } = require('../utils/file-utils');
const execAsync = promisify(exec);
// 获取上传目录路径
const uploadPath = getUploadPath();
@ -35,6 +39,17 @@ const fileFilter = (req, file, cb) => {
}
};
// 数据包上传文件过滤器
const packageFileFilter = (req, file, cb) => {
// 允许的文件类型:.h文件和包含.so的文件
const fileName = file.originalname.toLowerCase();
if (fileName.endsWith('.h') || fileName.includes('.so')) {
cb(null, true);
} else {
cb(new Error('数据包只能包含.h文件和动态库文件'));
}
};
const upload = multer({
storage: storage,
fileFilter: fileFilter,
@ -43,6 +58,15 @@ const upload = multer({
}
});
// 数据包上传专用multer配置
const packageUpload = multer({
storage: storage,
fileFilter: packageFileFilter,
limits: {
fileSize: 50 * 1024 * 1024 // 限制文件大小为50MB
}
});
// 读取目录内容
router.get('/readdir', async (req, res) => {
try {
@ -461,4 +485,297 @@ 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; // 从前端获取文件夹名称
if (!confName) {
return res.status(400).json({
success: false,
message: '未提供构型名称'
});
}
if (!req.files || req.files.length === 0) {
return res.status(400).json({
success: false,
message: '未提供文件'
});
}
// 验证是否为文件夹上传
const hasFolderStructure = req.files.some(file =>
file.originalname.includes('/') || file.originalname.includes('\\')
);
// 优先使用前端传递的文件夹名称,如果没有则从文件路径中提取
let packageName = null;
if (folderName) {
// 使用前端传递的文件夹名称
packageName = folderName;
} else if (hasFolderStructure) {
// 如果文件路径包含分隔符,从路径中提取
for (const file of req.files) {
const pathParts = file.originalname.replace(/\\/g, '/').split('/');
if (pathParts.length > 1) {
packageName = pathParts[0]; // 取第一级目录名作为数据包名称
break;
}
}
} else {
// 如果文件路径不包含分隔符尝试从webkitRelativePath获取
if (req.files.length > 0 && req.files[0].webkitRelativePath) {
const pathParts = req.files[0].webkitRelativePath.split('/');
if (pathParts.length > 1) {
packageName = pathParts[0];
}
}
// 如果还是无法获取,使用默认名称
if (!packageName) {
packageName = 'uploaded_package_' + Date.now();
}
}
if (!packageName) {
return res.status(400).json({
success: false,
message: '无法从上传的文件中提取文件夹名称'
});
}
// 验证文件夹内容
const fileTypes = {
headerFiles: [], // .h文件
libraryFiles: [] // 动态库文件
};
req.files.forEach(file => {
const fileName = path.basename(file.originalname);
const ext = path.extname(fileName).toLowerCase();
if (ext === '.h') {
fileTypes.headerFiles.push(file);
} else if (fileName.toLowerCase().includes('.so')) {
fileTypes.libraryFiles.push(file);
}
});
// 验证文件数量
if (fileTypes.headerFiles.length !== 1) {
return res.status(400).json({
success: false,
message: `文件夹必须包含且仅包含一个.h文件当前包含 ${fileTypes.headerFiles.length} 个.h文件`
});
}
if (fileTypes.libraryFiles.length !== 1) {
return res.status(400).json({
success: false,
message: `文件夹必须包含且仅包含一个动态库文件,当前包含 ${fileTypes.libraryFiles.length} 个动态库文件`
});
}
// 验证总文件数量
const totalFiles = fileTypes.headerFiles.length + fileTypes.libraryFiles.length;
if (totalFiles !== req.files.length) {
return res.status(400).json({
success: false,
message: `文件夹只能包含一个.h文件和一个动态库文件当前包含 ${req.files.length} 个文件`
});
}
// 获取数据包路径
const { getPackagesPath } = require('../utils/file-utils');
const packagesPath = getPackagesPath(confName);
if (!packagesPath) {
return res.status(400).json({
success: false,
message: '无法获取数据包路径'
});
}
// 创建目标数据包目录
const targetPackagePath = path.join(packagesPath, packageName);
try {
await fsPromises.mkdir(targetPackagePath, { recursive: true });
} catch (error) {
if (error.code !== 'EEXIST') {
console.error('创建数据包目录失败:', error);
return res.status(500).json({
success: false,
message: '创建数据包目录失败: ' + error.message
});
}
}
const uploadedFiles = [];
const errors = [];
// 处理每个上传的文件
for (const file of req.files) {
try {
// 从文件的相对路径中提取目标路径
let targetPath;
if (file.originalname.includes('/') || file.originalname.includes('\\')) {
// 如果文件名包含路径分隔符,说明是文件夹上传
const relativePath = file.originalname.replace(/\\/g, '/');
targetPath = path.join(targetPackagePath, relativePath);
} else {
// 单个文件,直接放在数据包根目录
targetPath = path.join(targetPackagePath, file.originalname);
}
// 确保目标目录存在
const targetDir = path.dirname(targetPath);
try {
await fsPromises.mkdir(targetDir, { recursive: true });
} catch (mkdirError) {
if (mkdirError.code !== 'EEXIST') {
throw mkdirError;
}
}
// 移动文件到目标位置
await fsPromises.copyFile(file.path, targetPath);
// 删除临时文件
try {
await fsPromises.unlink(file.path);
} catch (unlinkError) {
console.warn('删除临时文件失败:', unlinkError);
}
// 获取文件信息
const stats = await fsPromises.stat(targetPath);
const fileName = path.basename(targetPath);
const ext = path.extname(fileName).toLowerCase();
uploadedFiles.push({
name: fileName,
path: path.relative(targetPackagePath, targetPath),
size: stats.size,
created: stats.birthtime,
modified: stats.mtime,
type: ext === '.h' ? 'header' : 'library'
});
} catch (fileError) {
console.error(`处理文件 ${file.originalname} 失败:`, fileError);
errors.push({
file: file.originalname,
error: fileError.message
});
}
}
// 对动态库执行nm -D命令获取入口点函数信息
let entryPointInfo = null;
try {
// 找到动态库文件路径
const libraryFile = uploadedFiles.find(file => file.type === 'library');
if (libraryFile) {
const libraryPath = path.join(targetPackagePath, libraryFile.path);
entryPointInfo = await getEntryPointInfo(libraryPath);
}
} catch (error) {
console.warn('获取动态库入口点信息失败:', error.message);
// 不阻止上传流程,只记录警告
}
// 返回上传结果
res.json({
success: true,
message: `成功上传数据包 ${packageName},包含 ${fileTypes.headerFiles.length} 个头文件和 ${fileTypes.libraryFiles.length} 个动态库文件`,
packagePath: packageName, // 相对于Packages目录的路径
headerFile: path.basename(fileTypes.headerFiles[0].originalname),
libraryFile: path.basename(fileTypes.libraryFiles[0].originalname),
entryPoint: entryPointInfo ? entryPointInfo.symbolName : null,
paramType: entryPointInfo ? entryPointInfo.paramType : null
});
} catch (error) {
console.error('数据包上传失败:', error);
res.status(500).json({
success: false,
message: '数据包上传失败: ' + error.message
});
}
});
/**
* 获取动态库入口点函数信息
* @param {string} libraryPath - 动态库文件路径
* @returns {Promise<Object|null>} 入口点信息对象或null
*/
async function getEntryPointInfo(libraryPath) {
try {
// 执行nm -D命令获取动态符号表
const { stdout } = await execAsync(`nm -D "${libraryPath}" | grep EntryPoint`);
if (!stdout.trim()) {
console.log('未找到EntryPoint相关的符号');
return null;
}
// 解析nm输出只取第一个匹配的入口点
const lines = stdout.trim().split('\n');
if (lines.length > 0) {
const line = lines[0]; // 只处理第一行
// nm输出格式: 地址 类型 符号名
const parts = line.trim().split(/\s+/);
if (parts.length >= 3) {
const symbolName = parts[2];
// 尝试从函数名中提取参数类型信息
// 函数名格式示例: _Z28SACSCWeightBalanceEntryPointP20ComacDataStructure_S
let paramType = null;
if (symbolName.startsWith('_Z')) {
// 解析C++符号名格式
const match = symbolName.match(/_Z\d+([A-Za-z0-9_]+)P(\d+)([A-Za-z0-9_]+)/);
if (match) {
paramType = match[3];
} else {
// 如果无法解析,使用原始符号名
paramType = symbolName;
}
} else {
// C函数名使用原始符号名
paramType = symbolName;
}
const entryPoint = {
symbolName: symbolName,
paramType: paramType
};
console.log('动态库入口点信息:', entryPoint);
return entryPoint;
}
}
return null;
} catch (error) {
console.warn('获取动态库入口点信息失败:', error.message);
return null;
}
}
module.exports = router;

View File

@ -49,32 +49,64 @@ function getXNCorePath() {
return xnCorePath;
}
// 模型项目文件目录路径
function getProjectModelPath() {
// 获取当前构型路径
function getCurrentConfPath(confName) {
if (!confName) {
return '';
}
const xnCorePath = getXNCorePath();
if (!xnCorePath) return '';
return path.join(xnCorePath, 'Project', 'Model');
return path.join(xnCorePath, 'Configuration', confName);
}
// 模型项目文件目录路径
function getProjectModelPath(confName) {
if (!confName) {
return '';
}
const xnCorePath = getXNCorePath();
if (!xnCorePath) return '';
return path.join(xnCorePath, 'Configuration', confName, 'ModelProjects');
}
// 模型文件目录路径
function getModelPath() {
function getModelPath(confName) {
if (!confName) {
return '';
}
const xnCorePath = getXNCorePath();
if (!xnCorePath) return '';
return path.join(xnCorePath, 'Models');
return path.join(xnCorePath, 'Configuration', confName, 'Models');
}
//服务项目文件目录路径
function getProjectServicePath() {
function getProjectServicePath(confName) {
if (!confName) {
return '';
}
const xnCorePath = getXNCorePath();
if (!xnCorePath) return '';
return path.join(xnCorePath, 'Project', 'Service');
return path.join(xnCorePath, 'Configuration', confName, 'ServiceProjects');
}
// 服务文件目录路径
function getServicePath() {
function getServicePath(confName) {
if (!confName) {
return '';
}
const xnCorePath = getXNCorePath();
if (!xnCorePath) return '';
return path.join(xnCorePath, 'Services');
return path.join(xnCorePath, 'Configuration', confName, 'Services');
}
//数据包路径
function getPackagesPath(confName) {
if (!confName) {
return '';
}
const xnCorePath = getXNCorePath();
if (!xnCorePath) return '';
return path.join(xnCorePath, 'Configuration', confName, 'Packages');
}
// 日志目录路径处理
@ -152,9 +184,12 @@ function isAllowedFileType(fileName, allowedTypes = []) {
module.exports = {
getXNCorePath,
getCurrentConfPath,
getModelPath,
getProjectModelPath,
getProjectServicePath,
getServicePath,
getPackagesPath,
getActualLogPath,
isPathSafe,
ensureDirectoryExists,

View File

@ -59,10 +59,10 @@ function getModelVersionsByClassName(className, planeName) {
const query = `
SELECT
PlaneName, ClassName, Name, Version, CodePath, Author, Description,
PlaneName, ClassName, Name, ConfID, Version, Author, Description,
CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority,
DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName,
ConfID, CmdList
DataPackagePath, DataPackageName, DataPackageHeaderName, DataPackageEntryPoint, DataPackageInterfaceName,
InputStruct, OutputStruct, HeartStruct, CmdList
FROM 'XNModelsVersion'
WHERE ClassName = ? AND PlaneName = ?
ORDER BY Version DESC
@ -124,8 +124,8 @@ function saveModelVersion(versionData) {
PlaneName = ?,
ClassName = ?,
Name = ?,
ConfID = ?,
Version = ?,
CodePath = ?,
Author = ?,
Description = ?,
CreatTime = ?,
@ -134,18 +134,21 @@ function saveModelVersion(versionData) {
RunNode = ?,
Priority = ?,
DataPackagePath = ?,
DataPackageHeaderPath = ?,
DataPackageName = ?,
DataPackageHeaderName = ?,
DataPackageEntryPoint = ?,
DataPackageInterfaceName = ?,
ConfID = ?,
InputStruct = ?,
OutputStruct = ?,
HeartStruct = ?,
CmdList = ?
WHERE ClassName = ? AND Version = ? AND PlaneName = ?
`).run(
versionData.PlaneName,
versionData.ClassName,
versionData.Name,
versionData.ConfID,
versionData.Version,
versionData.CodePath || '',
versionData.Author,
versionData.Description || '',
versionData.CreatTime || changeTime,
@ -154,10 +157,13 @@ function saveModelVersion(versionData) {
versionData.RunNode || '',
versionData.Priority || '0',
versionData.DataPackagePath || '',
versionData.DataPackageHeaderPath || '',
versionData.DataPackageName || '',
versionData.DataPackageHeaderName || '',
versionData.DataPackageEntryPoint || '',
versionData.DataPackageInterfaceName || '',
versionData.ConfID,
versionData.InputStruct || '',
versionData.OutputStruct || '',
versionData.HeartStruct || '',
versionData.CmdList || '[]',
versionData.ClassName,
versionData.originalVersion || versionData.Version,
@ -210,17 +216,17 @@ function saveNewVersion(db, versionData) {
// 插入新版本
const insertResult = db.prepare(`
INSERT INTO 'XNModelsVersion' (
PlaneName, ClassName, Name, Version, CodePath, Author, Description,
PlaneName, ClassName, Name, ConfID, Version, Author, Description,
CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority,
DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName,
ConfID, CmdList
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
DataPackagePath, DataPackageName, DataPackageHeaderName, DataPackageEntryPoint, DataPackageInterfaceName,
InputStruct, OutputStruct, HeartStruct, CmdList
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(
versionData.PlaneName,
versionData.ClassName,
versionData.Name,
versionData.ConfID,
versionData.Version,
versionData.CodePath || '',
versionData.Author,
versionData.Description || '',
createTime,
@ -229,10 +235,13 @@ function saveNewVersion(db, versionData) {
versionData.RunNode || '',
versionData.Priority || '0',
versionData.DataPackagePath || '',
versionData.DataPackageHeaderPath || '',
versionData.DataPackageName || '',
versionData.DataPackageHeaderName || '',
versionData.DataPackageEntryPoint || '',
versionData.DataPackageInterfaceName || '',
versionData.ConfID,
versionData.InputStruct || '',
versionData.OutputStruct || '',
versionData.HeartStruct || '',
versionData.CmdList || '[]'
);