将运行测试和运行仿真合二为一
This commit is contained in:
parent
5ae0ecae52
commit
0296daa268
Binary file not shown.
@ -111,11 +111,8 @@ class ContentArea extends HTMLElement {
|
||||
case 'service-development':
|
||||
contentElement = document.createElement('service-development');
|
||||
break;
|
||||
case 'run-test':
|
||||
contentElement = document.createElement('run-test');
|
||||
break;
|
||||
case 'run-simulation':
|
||||
contentElement = document.createElement('run-simulation');
|
||||
case 'run-sim':
|
||||
contentElement = document.createElement('run-sim');
|
||||
break;
|
||||
case 'simulation-monitor':
|
||||
contentElement = document.createElement('simulation-monitor');
|
||||
|
@ -1,4 +1,4 @@
|
||||
class RunTest extends HTMLElement {
|
||||
class RunSim extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
@ -7,12 +7,17 @@ class RunTest extends HTMLElement {
|
||||
this.eventSource = null;
|
||||
this.logFileWatcher = null;
|
||||
this.logFilePollingInterval = null;
|
||||
this.currentSimulationId = null;
|
||||
this.reconnectAttempts = 0;
|
||||
this.maxReconnectAttempts = 3;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
this.loadModelGroups();
|
||||
this.loadServices();
|
||||
// 检查并连接到已有的仿真
|
||||
this.checkAndConnectToExistingSimulation();
|
||||
}
|
||||
|
||||
async loadModelGroups() {
|
||||
@ -280,12 +285,298 @@ class RunTest extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
// 连接到SSE事件源获取实时输出
|
||||
connectToEventSource(simulationId) {
|
||||
// 关闭之前的连接
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
}
|
||||
|
||||
// 创建新的SSE连接
|
||||
const url = `/api/simulation-output/${simulationId}`;
|
||||
this.eventSource = new EventSource(url);
|
||||
|
||||
// 标准输出和错误输出
|
||||
this.eventSource.addEventListener('output', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
const outputContent = this.shadowRoot.querySelector('#output-content');
|
||||
|
||||
// 添加新输出并应用ANSI颜色
|
||||
if (data.data) {
|
||||
const html = this.convertAnsiToHtml(data.data);
|
||||
outputContent.innerHTML += html;
|
||||
|
||||
// 自动滚动到底部
|
||||
outputContent.scrollTop = outputContent.scrollHeight;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('处理输出事件失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 仿真状态
|
||||
this.eventSource.addEventListener('status', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
if (data.running === false) {
|
||||
// 仿真已经不存在或已结束
|
||||
this.showMessage(data.message || '仿真不存在或已结束');
|
||||
|
||||
// 如果是进程不存在,尝试重新连接
|
||||
if (data.message === '仿真不存在或已结束' && this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||
this.reconnectAttempts++;
|
||||
this.showMessage(`尝试重新连接 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
|
||||
setTimeout(() => this.checkSimulationStatus(simulationId), 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
// 重置UI
|
||||
this.resetSimulationButtons();
|
||||
} else if (data.running === true) {
|
||||
// 更新状态为已连接
|
||||
this.showSuccess('已连接到运行中的仿真');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('处理状态事件失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 仿真完成
|
||||
this.eventSource.addEventListener('completed', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
if (data.success) {
|
||||
this.showSuccess('仿真执行成功');
|
||||
} else {
|
||||
this.showError(`仿真执行失败: ${data.message}`);
|
||||
}
|
||||
|
||||
// 重置UI
|
||||
this.resetSimulationButtons();
|
||||
} catch (error) {
|
||||
console.error('处理完成事件失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 仿真错误
|
||||
this.eventSource.addEventListener('error', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
this.showError(`仿真错误: ${data.message}`);
|
||||
|
||||
// 重置UI
|
||||
this.resetSimulationButtons();
|
||||
} catch (error) {
|
||||
console.error('处理错误事件失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 仿真终止
|
||||
this.eventSource.addEventListener('terminated', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
this.showMessage(`仿真已终止: ${data.message}`);
|
||||
|
||||
// 在输出框中添加终止信息
|
||||
const outputContent = this.shadowRoot.querySelector('#output-content');
|
||||
outputContent.innerHTML += `\n\n仿真已终止:${data.message}`;
|
||||
|
||||
// 重置UI
|
||||
this.resetSimulationButtons();
|
||||
} catch (error) {
|
||||
console.error('处理终止事件失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 连接错误处理
|
||||
this.eventSource.onerror = (error) => {
|
||||
console.error('SSE连接错误:', error);
|
||||
|
||||
// 检查连接状态
|
||||
if (this.eventSource && this.eventSource.readyState === 2) { // CLOSED
|
||||
// 如果还有重连次数,尝试重新连接
|
||||
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||
this.reconnectAttempts++;
|
||||
this.showMessage(`连接断开,尝试重新连接 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
|
||||
setTimeout(() => this.checkSimulationStatus(this.currentSimulationId), 1000);
|
||||
} else {
|
||||
this.showError('实时输出连接已断开');
|
||||
this.resetSimulationButtons();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 检查仿真状态
|
||||
async checkSimulationStatus(simulationId) {
|
||||
try {
|
||||
const response = await fetch(`/api/check-process/${simulationId}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.running) {
|
||||
// 仿真仍在运行,重新连接到事件源
|
||||
this.showMessage('重新连接到运行中的仿真...');
|
||||
this.connectToEventSource(simulationId);
|
||||
} else {
|
||||
// 仿真已经停止,重置UI
|
||||
this.showMessage('仿真已结束');
|
||||
this.resetSimulationButtons();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查仿真状态失败:', error);
|
||||
// 假设仿真已停止
|
||||
this.resetSimulationButtons();
|
||||
}
|
||||
}
|
||||
|
||||
async runSimulation() {
|
||||
const savedSelection = localStorage.getItem('xnsim-selection');
|
||||
const selection = savedSelection ? JSON.parse(savedSelection) : {};
|
||||
const confID = selection.configurationId;
|
||||
|
||||
try {
|
||||
this.showMessage('准备运行仿真...');
|
||||
const runButton = this.shadowRoot.querySelector('#run-simulation-button');
|
||||
const pauseButton = this.shadowRoot.querySelector('#pause-simulation-button');
|
||||
const stopButton = this.shadowRoot.querySelector('#stop-simulation-button');
|
||||
|
||||
runButton.disabled = true;
|
||||
pauseButton.disabled = false;
|
||||
stopButton.disabled = false;
|
||||
runButton.textContent = '运行中...';
|
||||
|
||||
// 清空并初始化输出框
|
||||
const outputContent = this.shadowRoot.querySelector('#output-content');
|
||||
outputContent.innerHTML = '开始执行仿真...\n';
|
||||
|
||||
// 准备启动参数
|
||||
const simulationArgs = ['-id', confID];
|
||||
|
||||
// 调用后端API执行仿真
|
||||
const response = await fetch('/api/run-simulation', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
args: simulationArgs
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
outputContent.innerHTML += `\n错误: ${errorData.message || '仿真执行失败'}\n`;
|
||||
if (errorData.output) outputContent.innerHTML += this.convertAnsiToHtml(errorData.output);
|
||||
if (errorData.errorOutput) outputContent.innerHTML += this.convertAnsiToHtml(errorData.errorOutput);
|
||||
throw new Error(errorData.message || '仿真执行失败');
|
||||
}
|
||||
|
||||
const responseData = await response.json();
|
||||
|
||||
// 获取进程ID
|
||||
const simulationId = responseData.simulationId;
|
||||
|
||||
// 保存当前仿真ID
|
||||
this.currentSimulationId = simulationId;
|
||||
|
||||
// 建立SSE连接
|
||||
this.connectToEventSource(simulationId);
|
||||
|
||||
// 根据启动结果更新UI
|
||||
if (responseData.success) {
|
||||
this.showSuccess('仿真已启动');
|
||||
} else {
|
||||
this.showError(`仿真启动失败: ${responseData.message || '未知错误'}`);
|
||||
this.resetSimulationButtons();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('执行仿真失败:', error);
|
||||
this.showError('执行仿真失败: ' + error.message);
|
||||
|
||||
// 重置按钮状态
|
||||
this.resetSimulationButtons();
|
||||
|
||||
// 显示错误详情
|
||||
const outputContent = this.shadowRoot.querySelector('#output-content');
|
||||
outputContent.innerHTML += `\n\n执行错误: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加新方法:检查并连接到已有的仿真
|
||||
async checkAndConnectToExistingSimulation() {
|
||||
try {
|
||||
const response = await fetch('/api/check-xnengine');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.running) {
|
||||
this.showMessage('检测到正在运行的仿真,正在重新连接...');
|
||||
|
||||
// 更新UI以反映运行状态
|
||||
const runButton = this.shadowRoot.querySelector('#run-simulation-button');
|
||||
const pauseButton = this.shadowRoot.querySelector('#pause-simulation-button');
|
||||
const stopButton = this.shadowRoot.querySelector('#stop-simulation-button');
|
||||
|
||||
if (runButton) {
|
||||
runButton.disabled = true;
|
||||
runButton.textContent = '运行中...';
|
||||
}
|
||||
|
||||
if (pauseButton) {
|
||||
pauseButton.disabled = false;
|
||||
}
|
||||
|
||||
if (stopButton) {
|
||||
stopButton.disabled = false;
|
||||
}
|
||||
|
||||
// 使用进程ID作为仿真ID重新连接
|
||||
this.currentSimulationId = data.pid.toString();
|
||||
|
||||
// 清空并初始化输出框
|
||||
const outputContent = this.shadowRoot.querySelector('#output-content');
|
||||
outputContent.innerHTML = '重新连接到运行中的仿真...\n';
|
||||
|
||||
// 连接到SSE获取输出
|
||||
this.connectToEventSource(this.currentSimulationId);
|
||||
|
||||
// 更新状态为已连接
|
||||
this.showSuccess('已连接到运行中的仿真');
|
||||
|
||||
// 重置重连尝试次数
|
||||
this.reconnectAttempts = 0;
|
||||
} else {
|
||||
// 如果没有运行中的仿真,重置UI
|
||||
this.resetSimulationButtons();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查XNEngine进程失败:', error);
|
||||
this.showError('检查仿真状态失败: ' + error.message);
|
||||
this.resetSimulationButtons();
|
||||
}
|
||||
}
|
||||
|
||||
// 添加reactivate方法,用于从缓存中恢复时检查仿真状态
|
||||
async reactivate() {
|
||||
// 检查是否有XNEngine进程在运行
|
||||
await this.checkAndConnectToExistingSimulation();
|
||||
}
|
||||
|
||||
// 在组件销毁时清理资源
|
||||
disconnectedCallback() {
|
||||
if (this.logFilePollingInterval) {
|
||||
clearInterval(this.logFilePollingInterval);
|
||||
this.logFilePollingInterval = null;
|
||||
}
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
this.eventSource = null;
|
||||
}
|
||||
this.currentSimulationId = null;
|
||||
this.reconnectAttempts = 0;
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
@ -477,6 +768,47 @@ class RunTest extends HTMLElement {
|
||||
background-color: #cccccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.simulation-button {
|
||||
background-color: #4caf50;
|
||||
}
|
||||
|
||||
.simulation-button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
.simulation-button:disabled {
|
||||
background-color: #cccccc;
|
||||
}
|
||||
|
||||
.pause-button {
|
||||
background-color: #ff9800;
|
||||
}
|
||||
|
||||
.pause-button:hover {
|
||||
background-color: #f57c00;
|
||||
}
|
||||
|
||||
.pause-button:disabled {
|
||||
background-color: #cccccc;
|
||||
}
|
||||
|
||||
.stop-button {
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
.stop-button:hover {
|
||||
background-color: #d32f2f;
|
||||
}
|
||||
|
||||
.stop-button:disabled {
|
||||
background-color: #cccccc;
|
||||
}
|
||||
</style>
|
||||
<div class="test-container">
|
||||
<div class="content-container">
|
||||
@ -494,7 +826,12 @@ class RunTest extends HTMLElement {
|
||||
<div class="output-header">
|
||||
<span>运行输出</span>
|
||||
<div id="message"></div>
|
||||
<button id="run-button">运行测试</button>
|
||||
<div class="button-group">
|
||||
<button id="run-button">运行测试</button>
|
||||
<button id="run-simulation-button" class="simulation-button">运行仿真</button>
|
||||
<button id="pause-simulation-button" class="pause-button" disabled>暂停仿真</button>
|
||||
<button id="stop-simulation-button" class="stop-button" disabled>结束仿真</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="output-content" class="output-content">等待测试运行...</div>
|
||||
</div>
|
||||
@ -505,6 +842,15 @@ class RunTest extends HTMLElement {
|
||||
// 添加事件监听器
|
||||
const runButton = this.shadowRoot.querySelector('#run-button');
|
||||
runButton.addEventListener('click', () => this.runTest());
|
||||
|
||||
const runSimulationButton = this.shadowRoot.querySelector('#run-simulation-button');
|
||||
runSimulationButton.addEventListener('click', () => this.runSimulation());
|
||||
|
||||
const pauseSimulationButton = this.shadowRoot.querySelector('#pause-simulation-button');
|
||||
pauseSimulationButton.addEventListener('click', () => this.pauseSimulation());
|
||||
|
||||
const stopSimulationButton = this.shadowRoot.querySelector('#stop-simulation-button');
|
||||
stopSimulationButton.addEventListener('click', () => this.stopSimulation());
|
||||
}
|
||||
|
||||
// ANSI终端颜色转换为HTML
|
||||
@ -618,6 +964,87 @@ class RunTest extends HTMLElement {
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
async pauseSimulation() {
|
||||
const simulationId = this.currentSimulationId;
|
||||
if (!simulationId) return;
|
||||
|
||||
try {
|
||||
const button = this.shadowRoot.querySelector('#pause-simulation-button');
|
||||
const isPaused = button.textContent === '继续仿真';
|
||||
|
||||
// 调用后端API暂停/继续仿真
|
||||
const response = await fetch('/api/pause-simulation', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: simulationId,
|
||||
pause: !isPaused
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('操作失败');
|
||||
}
|
||||
|
||||
// 更新按钮状态
|
||||
button.textContent = isPaused ? '暂停仿真' : '继续仿真';
|
||||
this.showMessage(isPaused ? '仿真已继续' : '仿真已暂停');
|
||||
} catch (error) {
|
||||
console.error('暂停/继续仿真失败:', error);
|
||||
this.showError('操作失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async stopSimulation() {
|
||||
const simulationId = this.currentSimulationId;
|
||||
if (!simulationId) return;
|
||||
|
||||
try {
|
||||
// 调用后端API停止仿真
|
||||
const response = await fetch('/api/stop-simulation', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: simulationId
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('停止仿真失败');
|
||||
}
|
||||
|
||||
// 关闭SSE连接
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
this.eventSource = null;
|
||||
}
|
||||
|
||||
// 重置按钮状态
|
||||
this.resetSimulationButtons();
|
||||
this.currentSimulationId = null;
|
||||
this.showSuccess('仿真已停止');
|
||||
} catch (error) {
|
||||
console.error('停止仿真失败:', error);
|
||||
this.showError('停止仿真失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
resetSimulationButtons() {
|
||||
const runButton = this.shadowRoot.querySelector('#run-simulation-button');
|
||||
const pauseButton = this.shadowRoot.querySelector('#pause-simulation-button');
|
||||
const stopButton = this.shadowRoot.querySelector('#stop-simulation-button');
|
||||
|
||||
runButton.disabled = false;
|
||||
runButton.textContent = '运行仿真';
|
||||
pauseButton.disabled = true;
|
||||
stopButton.disabled = true;
|
||||
pauseButton.textContent = '暂停仿真';
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('run-test', RunTest);
|
||||
customElements.define('run-sim', RunSim);
|
File diff suppressed because it is too large
Load Diff
@ -239,10 +239,6 @@ class SubToolbar extends HTMLElement {
|
||||
</div>
|
||||
<!-- 运行子菜单 -->
|
||||
<div class="sub-menu" data-parent="run">
|
||||
<div class="sub-item" data-icon="flask">
|
||||
<img src="assets/icons/png/flask.png" alt="运行测试" class="icon">
|
||||
运行测试
|
||||
</div>
|
||||
<div class="sub-item" data-icon="rocket">
|
||||
<img src="assets/icons/png/rocket.png" alt="运行仿真" class="icon">
|
||||
运行仿真
|
||||
@ -345,7 +341,6 @@ class SubToolbar extends HTMLElement {
|
||||
const toolIcons = {
|
||||
'home': { icon: 'home', text: '主页' },
|
||||
'develop': { icon: 'develop', text: '开发' },
|
||||
'config': { icon: 'sliders', text: '配置' },
|
||||
'run': { icon: 'play', text: '运行' },
|
||||
'monitor': { icon: 'chart', text: '监控' },
|
||||
'system': { icon: 'cogs', text: '管理' }
|
||||
|
@ -21,8 +21,7 @@
|
||||
<script src="components/system-log.js"></script>
|
||||
<script src="components/configuration-config.js"></script>
|
||||
<script src="components/interface-config.js" type="module"></script>
|
||||
<script src="components/run-test.js"></script>
|
||||
<script src="components/run-simulation.js"></script>
|
||||
<script src="components/run-sim.js"></script>
|
||||
<script src="components/simulation-monitor.js"></script>
|
||||
<script src="components/model-monitor.js"></script>
|
||||
<script src="components/data-monitor.js"></script>
|
||||
@ -551,15 +550,8 @@
|
||||
}
|
||||
|
||||
// 处理运行测试标签页
|
||||
if (title === '运行测试') {
|
||||
const id = 'run-test';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理运行仿真标签页
|
||||
if (title === '运行仿真') {
|
||||
const id = 'run-simulation';
|
||||
const id = 'run-sim';
|
||||
tabsContainer.createTab(id, title, icon, parentText, parentTool);
|
||||
return;
|
||||
}
|
||||
@ -622,7 +614,6 @@
|
||||
'Q&A': 'question',
|
||||
'构型配置': 'chip',
|
||||
'接口配置': 'plug',
|
||||
'运行测试': 'flask',
|
||||
'运行仿真': 'rocket',
|
||||
'仿真监控': 'desktop',
|
||||
'模型监控': 'cubes',
|
||||
|
Loading…
x
Reference in New Issue
Block a user