XNSim/XNSimHtml/components/run-env-config/environment-section.js
2025-04-28 12:25:20 +08:00

427 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 环境配置模块
* @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;
}