XNSim/XNSimHtml/components/run-env-config/environment-section.js

427 lines
18 KiB
JavaScript
Raw Normal View History

2025-04-28 12:25:20 +08:00
/**
* 环境配置模块
* @type {module}
*/
import { createModal, markEdited } from './utils.js';
/**
* 渲染环境部分
* @param {HTMLElement} component - 组件实例
* @param {HTMLElement} container - 容器元素
* @param {Element} rootElement - XML根元素
*/
export function renderEnvironmentSection(component, container, rootElement) {
const envElement = rootElement.querySelector('Environment');
if (!envElement) {
return; // 如果找不到Environment元素直接返回
}
const section = document.createElement('div');
section.className = 'editor-section';
const title = document.createElement('div');
title.className = 'section-title';
title.textContent = '环境配置';
section.appendChild(title);
const propertyGroup = document.createElement('div');
propertyGroup.className = 'property-group';
// 获取并显示所有Environment属性
Array.from(envElement.attributes).forEach(attr => {
const propertyItem = document.createElement('div');
propertyItem.className = 'property-item';
const label = document.createElement('label');
label.className = 'property-label';
label.textContent = attr.name;
// 对CPUAffinity属性使用特殊处理
if (attr.name === 'CPUAffinity') {
const inputContainer = document.createElement('div');
inputContainer.style.display = 'flex';
inputContainer.style.position = 'relative';
const input = document.createElement('input');
input.className = 'property-input';
input.type = 'text';
input.value = attr.value;
input.dataset.path = `Environment@${attr.name}`;
input.style.flexGrow = '1';
input.title = 'CPU亲和性';
input.addEventListener('change', () => {
component.isEdited = markEdited(component.shadowRoot, component.isEdited);
});
const dropdownButton = document.createElement('button');
dropdownButton.className = 'dropdown-button';
dropdownButton.type = 'button'; // 确保按钮类型为button
dropdownButton.innerHTML = '▼';
dropdownButton.style.width = '30px';
dropdownButton.style.border = '1px solid #ddd';
dropdownButton.style.borderLeft = 'none';
dropdownButton.style.borderRadius = '0 4px 4px 0';
dropdownButton.style.backgroundColor = '#f5f5f5';
dropdownButton.style.cursor = 'pointer';
// 创建下拉框容器
const dropdown = document.createElement('div');
dropdown.className = 'cpu-dropdown';
dropdown.style.position = 'absolute';
dropdown.style.top = '100%';
dropdown.style.left = '0';
dropdown.style.right = '0';
dropdown.style.backgroundColor = 'white';
dropdown.style.border = '1px solid #ddd';
dropdown.style.borderRadius = '4px';
dropdown.style.boxShadow = '0 2px 10px rgba(0,0,0,0.1)';
dropdown.style.zIndex = '50'; // 提高z-index确保在最上层
dropdown.style.padding = '10px';
dropdown.style.display = 'none';
// 生成CPU核心选项
const cpuCount = 16; // 假设最多16个核心
const selectedCores = attr.value.split(',').map(core => parseInt(core.trim())).filter(core => !isNaN(core));
for (let i = 0; i < cpuCount; i++) {
const checkboxContainer = document.createElement('div');
checkboxContainer.style.marginBottom = '6px';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `cpu-${i}`;
checkbox.value = i;
checkbox.checked = selectedCores.includes(i);
checkbox.style.marginRight = '6px';
const checkboxLabel = document.createElement('label');
checkboxLabel.htmlFor = `cpu-${i}`;
checkboxLabel.textContent = `CPU ${i}`;
checkbox.addEventListener('change', () => {
// 获取所有选中的核心
const checkedCores = Array.from(dropdown.querySelectorAll('input[type="checkbox"]:checked'))
.map(cb => cb.value)
.sort((a, b) => a - b);
// 更新输入框的值
input.value = checkedCores.join(',');
input.dispatchEvent(new Event('change'));
});
checkboxContainer.appendChild(checkbox);
checkboxContainer.appendChild(checkboxLabel);
dropdown.appendChild(checkboxContainer);
}
// 添加应用和取消按钮
const buttonContainer = document.createElement('div');
buttonContainer.style.display = 'flex';
buttonContainer.style.justifyContent = 'flex-end';
buttonContainer.style.marginTop = '10px';
buttonContainer.style.gap = '8px';
const closeButton = document.createElement('button');
closeButton.textContent = '关闭';
closeButton.style.padding = '6px 12px';
closeButton.style.backgroundColor = '#f0f0f0';
closeButton.style.border = '1px solid #ddd';
closeButton.style.borderRadius = '4px';
closeButton.style.cursor = 'pointer';
closeButton.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
dropdown.style.display = 'none';
});
buttonContainer.appendChild(closeButton);
dropdown.appendChild(buttonContainer);
// 使用更可靠的方法显示下拉菜单
const toggleDropdown = (e) => {
e.preventDefault();
e.stopPropagation();
if (dropdown.style.display === 'none' || !dropdown.style.display) {
dropdown.style.display = 'block';
// 添加全局一次性点击事件,用于关闭下拉框
setTimeout(() => {
const closeDropdown = (event) => {
if (!dropdown.contains(event.target) && event.target !== dropdownButton) {
dropdown.style.display = 'none';
document.removeEventListener('click', closeDropdown);
}
};
document.addEventListener('click', closeDropdown);
}, 0);
} else {
dropdown.style.display = 'none';
}
};
// 点击下拉按钮时显示或隐藏下拉框
dropdownButton.addEventListener('click', toggleDropdown);
inputContainer.appendChild(input);
inputContainer.appendChild(dropdownButton);
inputContainer.appendChild(dropdown);
propertyItem.appendChild(label);
propertyItem.appendChild(inputContainer);
} else if (attr.name === 'BaseFrequency') {
// 对数字类型的属性使用number类型输入框
const input = document.createElement('input');
input.className = 'property-input';
input.type = 'number';
input.min = '0';
input.value = attr.value;
input.dataset.path = `Environment@${attr.name}`;
input.title = '仿真运行基础频率单位Hz';
input.addEventListener('change', () => {
component.isEdited = markEdited(component.shadowRoot, component.isEdited);
});
propertyItem.appendChild(label);
propertyItem.appendChild(input);
} else if (attr.name === 'OSName') {
const input = document.createElement('input');
input.className = 'property-input';
input.type = 'text';
input.value = attr.value;
input.dataset.path = `Environment@${attr.name}`;
input.title = '操作系统名称';
input.addEventListener('change', () => {
component.isEdited = markEdited(component.shadowRoot, component.isEdited);
});
propertyItem.appendChild(label);
propertyItem.appendChild(input);
} else if (attr.name === 'Version') {
const input = document.createElement('input');
input.className = 'property-input';
input.type = 'text';
input.value = attr.value;
input.dataset.path = `Environment@${attr.name}`;
input.title = '操作系统版本';
input.addEventListener('change', () => {
component.isEdited = markEdited(component.shadowRoot, component.isEdited);
});
propertyItem.appendChild(label);
propertyItem.appendChild(input);
} else if (attr.name === 'RTXVersion') {
const input = document.createElement('input');
input.className = 'property-input';
input.type = 'text';
input.value = attr.value;
input.dataset.path = `Environment@${attr.name}`;
input.title = '实时内核版本';
input.addEventListener('change', () => {
component.isEdited = markEdited(component.shadowRoot, component.isEdited);
});
propertyItem.appendChild(label);
propertyItem.appendChild(input);
} else if (attr.name === 'WorkPath') {
const input = document.createElement('input');
input.className = 'property-input';
input.type = 'text';
input.value = attr.value;
input.dataset.path = `Environment@${attr.name}`;
input.title = '仿真工作路径XNCore环境变量所在路径';
input.addEventListener('change', () => {
component.isEdited = markEdited(component.shadowRoot, component.isEdited);
});
propertyItem.appendChild(label);
propertyItem.appendChild(input);
} else if (attr.name === 'ModelsPath') {
const input = document.createElement('input');
input.className = 'property-input';
input.type = 'text';
input.value = attr.value;
input.dataset.path = `Environment@${attr.name}`;
input.title = '模型相对路径,相对于仿真工作路径';
input.addEventListener('change', () => {
component.isEdited = markEdited(component.shadowRoot, component.isEdited);
});
propertyItem.appendChild(label);
propertyItem.appendChild(input);
} else if (attr.name === 'ServicesPath') {
const input = document.createElement('input');
input.className = 'property-input';
input.type = 'text';
input.value = attr.value;
input.dataset.path = `Environment@${attr.name}`;
input.title = '服务相对路径,相对于仿真工作路径';
input.addEventListener('change', () => {
component.isEdited = markEdited(component.shadowRoot, component.isEdited);
});
propertyItem.appendChild(label);
propertyItem.appendChild(input);
} else if (attr.name === 'DomainID') {
// 对数字类型的属性使用number类型输入框
const input = document.createElement('input');
input.className = 'property-input';
input.type = 'number';
input.min = '0';
input.value = attr.value;
input.dataset.path = `Environment@${attr.name}`;
input.title = 'DDS通信域ID';
input.addEventListener('change', () => {
component.isEdited = markEdited(component.shadowRoot, component.isEdited);
});
propertyItem.appendChild(label);
propertyItem.appendChild(input);
} else {
const input = document.createElement('input');
input.className = 'property-input';
input.type = 'text';
input.value = attr.value;
input.dataset.path = `Environment@${attr.name}`;
input.addEventListener('change', () => {
component.isEdited = markEdited(component.shadowRoot, component.isEdited);
});
propertyItem.appendChild(label);
propertyItem.appendChild(input);
}
propertyGroup.appendChild(propertyItem);
});
section.appendChild(propertyGroup);
// 添加"添加新属性"按钮
const addButton = document.createElement('button');
addButton.className = 'add-button';
addButton.textContent = '添加新属性';
addButton.title = '添加新的运行环境属性';
addButton.addEventListener('click', () => showAddPropertyDialog(component, 'Environment'));
section.appendChild(addButton);
container.appendChild(section);
}
/**
* 添加新属性对话框
* @param {HTMLElement} component - 组件实例
* @param {string} elementName - 元素名称
*/
export function showAddPropertyDialog(component, elementName) {
// 创建表单内容
const formContent = document.createElement('div');
formContent.className = 'modal-form';
// 属性名输入
const nameRow = document.createElement('div');
nameRow.className = 'form-row';
const nameLabel = document.createElement('label');
nameLabel.className = 'form-label';
nameLabel.textContent = '属性名称';
const nameInput = document.createElement('input');
nameInput.className = 'form-input';
nameInput.type = 'text';
nameInput.placeholder = '请输入属性名称';
nameRow.appendChild(nameLabel);
nameRow.appendChild(nameInput);
formContent.appendChild(nameRow);
// 属性值输入
const valueRow = document.createElement('div');
valueRow.className = 'form-row';
const valueLabel = document.createElement('label');
valueLabel.className = 'form-label';
valueLabel.textContent = '属性值';
const valueInput = document.createElement('input');
valueInput.className = 'form-input';
valueInput.type = 'text';
valueInput.placeholder = '请输入属性值';
valueRow.appendChild(valueLabel);
valueRow.appendChild(valueInput);
formContent.appendChild(valueRow);
// 显示对话框
createModal(component.shadowRoot, '添加新属性', formContent, () => {
const propertyName = nameInput.value.trim();
const propertyValue = valueInput.value;
if (propertyName) {
addNewProperty(component, elementName, propertyName, propertyValue);
}
});
}
/**
* 添加新属性
* @param {HTMLElement} component - 组件实例
* @param {string} elementName - 元素名称
* @param {string} propertyName - 属性名
* @param {string} propertyValue - 属性值
*/
export function addNewProperty(component, elementName, propertyName, propertyValue) {
// 更新UI
const section = Array.from(component.shadowRoot.querySelectorAll('.section-title')).find(el => {
if (elementName === 'Environment' && el.textContent === '环境配置') return true;
return false;
})?.parentElement;
if (section) {
const propertyGroup = section.querySelector('.property-group');
if (propertyGroup) {
const propertyItem = document.createElement('div');
propertyItem.className = 'property-item';
const label = document.createElement('label');
label.className = 'property-label';
label.textContent = propertyName;
const input = document.createElement('input');
input.className = 'property-input';
input.type = 'text';
input.value = propertyValue;
input.dataset.path = `${elementName}@${propertyName}`;
input.dataset.isNew = 'true';
input.addEventListener('change', () => {
component.isEdited = markEdited(component.shadowRoot, component.isEdited);
});
propertyItem.appendChild(label);
propertyItem.appendChild(input);
propertyGroup.appendChild(propertyItem);
component.isEdited = markEdited(component.shadowRoot, component.isEdited);
// 静默更新XML
if (component.updateXmlFromVisualEditor) {
component.updateXmlFromVisualEditor(true);
}
}
}
}
/**
* 获取CPU亲和性设置
* @param {Document} xmlDoc - XML文档
* @returns {number[]} 可用的CPU核心列表
*/
export function getCPUAffinityOptions(xmlDoc) {
// 从环境配置中获取CPUAffinity的值
const envElement = xmlDoc.querySelector('Environment');
if (!envElement) return [];
const cpuAffinity = envElement.getAttribute('CPUAffinity') || '0';
const availableCores = cpuAffinity.split(',').map(core => parseInt(core.trim())).filter(core => !isNaN(core));
return availableCores;
}