427 lines
18 KiB
JavaScript
427 lines
18 KiB
JavaScript
/**
|
||
* 环境配置模块
|
||
* @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;
|
||
}
|