增加了模型监控,但有一些bug
This commit is contained in:
parent
051a2562be
commit
fb2dd9a57a
Binary file not shown.
@ -1,5 +1,6 @@
|
|||||||
#include "ModelInfoMonitor.h"
|
#include "ModelInfoMonitor.h"
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
@ -2,10 +2,236 @@ class ModelMonitor extends HTMLElement {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.attachShadow({ mode: 'open' });
|
this.attachShadow({ mode: 'open' });
|
||||||
|
this.monitorStatus = {
|
||||||
|
isMonitoring: false,
|
||||||
|
lastError: null
|
||||||
|
};
|
||||||
|
this.modelInfo = null;
|
||||||
|
this.statusCheckInterval = null;
|
||||||
|
this.chart = null;
|
||||||
|
this.chartInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.render();
|
this.render();
|
||||||
|
// 等待组件完全加载
|
||||||
|
setTimeout(() => {
|
||||||
|
this.initializeComponent();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeComponent() {
|
||||||
|
if (this.chartInitialized) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.initChart();
|
||||||
|
this.startStatusCheck();
|
||||||
|
this.chartInitialized = true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化组件失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
this.stopStatusCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
startStatusCheck() {
|
||||||
|
this.checkMonitorStatus();
|
||||||
|
this.statusCheckInterval = setInterval(() => {
|
||||||
|
this.checkMonitorStatus();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopStatusCheck() {
|
||||||
|
if (this.statusCheckInterval) {
|
||||||
|
clearInterval(this.statusCheckInterval);
|
||||||
|
this.statusCheckInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkMonitorStatus() {
|
||||||
|
try {
|
||||||
|
// 获取监控状态
|
||||||
|
const statusResponse = await fetch('/api/model-monitor/status');
|
||||||
|
const statusData = await statusResponse.json();
|
||||||
|
this.monitorStatus = statusData;
|
||||||
|
|
||||||
|
if (this.monitorStatus.isMonitoring) {
|
||||||
|
try {
|
||||||
|
// 获取模型信息
|
||||||
|
const modelResponse = await fetch('/api/model-monitor/model-info');
|
||||||
|
const modelData = await modelResponse.json();
|
||||||
|
|
||||||
|
if (!modelResponse.ok) {
|
||||||
|
console.error('获取模型信息失败:', modelData.error || modelData.message);
|
||||||
|
this.monitorStatus.lastError = modelData.error || modelData.message;
|
||||||
|
this.modelInfo = null;
|
||||||
|
} else {
|
||||||
|
this.modelInfo = modelData.data;
|
||||||
|
this.monitorStatus.lastError = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取模型信息失败:', error);
|
||||||
|
this.monitorStatus.lastError = error.message;
|
||||||
|
this.modelInfo = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.modelInfo = null;
|
||||||
|
this.monitorStatus.lastError = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateUI();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取监控状态失败:', error);
|
||||||
|
this.monitorStatus.lastError = error.message;
|
||||||
|
this.modelInfo = null;
|
||||||
|
this.updateUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusDisplay(status) {
|
||||||
|
switch (status) {
|
||||||
|
case 0:
|
||||||
|
return { text: '未运行', color: '#999999' };
|
||||||
|
case 1:
|
||||||
|
return { text: '运行中', color: '#4CAF50' };
|
||||||
|
case 2:
|
||||||
|
return { text: '暂停中', color: '#FF9800' };
|
||||||
|
case 3:
|
||||||
|
return { text: '错误', color: '#f44336' };
|
||||||
|
default:
|
||||||
|
return { text: '未知状态', color: '#f44336' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUI() {
|
||||||
|
const input = this.shadowRoot.querySelector('.domain-input');
|
||||||
|
const startButton = this.shadowRoot.querySelector('.start-button');
|
||||||
|
const stopButton = this.shadowRoot.querySelector('.stop-button');
|
||||||
|
const statusDisplay = this.shadowRoot.querySelector('.status-display');
|
||||||
|
const modelTableBody = this.shadowRoot.querySelector('#model-table-body');
|
||||||
|
|
||||||
|
if (this.monitorStatus.isMonitoring) {
|
||||||
|
input.disabled = true;
|
||||||
|
startButton.disabled = true;
|
||||||
|
stopButton.disabled = false;
|
||||||
|
} else {
|
||||||
|
input.disabled = false;
|
||||||
|
startButton.disabled = false;
|
||||||
|
stopButton.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新状态显示
|
||||||
|
statusDisplay.textContent = `监控状态: ${this.monitorStatus.isMonitoring ? '运行中' : '已停止'}`;
|
||||||
|
if (this.monitorStatus.lastError) {
|
||||||
|
statusDisplay.textContent += ` (错误: ${this.monitorStatus.lastError})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新模型表格
|
||||||
|
if (this.monitorStatus.isMonitoring && this.modelInfo && Array.isArray(this.modelInfo)) {
|
||||||
|
modelTableBody.innerHTML = this.modelInfo.map(model => {
|
||||||
|
const statusInfo = this.getStatusDisplay(model.status);
|
||||||
|
return `
|
||||||
|
<tr>
|
||||||
|
<td>${model.name || '未知'}</td>
|
||||||
|
<td>${model.id || '未知'}</td>
|
||||||
|
<td style="color: ${statusInfo.color}">${statusInfo.text}</td>
|
||||||
|
<td>${model.threadId || '未知'}</td>
|
||||||
|
<td>${model.node}</td>
|
||||||
|
<td>${model.priority || '未知'}</td>
|
||||||
|
<td>${model.runCount || '0'}</td>
|
||||||
|
<td>${(model.setFrequency || 0).toFixed(2)}</td>
|
||||||
|
<td>${(model.avgFrequency || 0).toFixed(2)}</td>
|
||||||
|
<td>${(model.maxFrequency || 0).toFixed(2)}</td>
|
||||||
|
<td>${(model.minFrequency || 0).toFixed(2)}</td>
|
||||||
|
<td>${(model.setPeriod || 0).toFixed(2)}</td>
|
||||||
|
<td>${(model.avgPeriod || 0).toFixed(2)}</td>
|
||||||
|
<td>${(model.maxPeriod || 0).toFixed(2)}</td>
|
||||||
|
<td>${(model.minPeriod || 0).toFixed(2)}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
// 更新图表数据
|
||||||
|
this.updateChartData();
|
||||||
|
} else {
|
||||||
|
modelTableBody.innerHTML = '<tr><td colspan="14" style="text-align: center;">暂无模型信息</td></tr>';
|
||||||
|
// 清空图表数据
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.data.labels = [];
|
||||||
|
this.chart.data.datasets = [];
|
||||||
|
this.chart.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async startMonitoring() {
|
||||||
|
const domainId = this.shadowRoot.querySelector('.domain-input').value.trim();
|
||||||
|
|
||||||
|
// 验证域ID是否为有效的数字字符串
|
||||||
|
if (!/^\d+$/.test(domainId)) {
|
||||||
|
console.error('域ID必须是有效的数字');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 首先检查DDS监控状态
|
||||||
|
const ddsStatusResponse = await fetch('/api/dds-monitor/status');
|
||||||
|
const ddsStatusData = await ddsStatusResponse.json();
|
||||||
|
|
||||||
|
// 如果DDS监控未初始化,先初始化DDS监控
|
||||||
|
if (!ddsStatusData.isInitialized) {
|
||||||
|
const ddsInitResponse = await fetch('/api/dds-monitor/initialize', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ domainId })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!ddsInitResponse.ok) {
|
||||||
|
const errorData = await ddsInitResponse.json();
|
||||||
|
console.error('DDS监控初始化失败:', errorData.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动模型监控
|
||||||
|
const response = await fetch('/api/model-monitor/start', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ domainId })
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
this.monitorStatus = data.status;
|
||||||
|
this.updateUI();
|
||||||
|
} else {
|
||||||
|
console.error('启动监控失败:', data.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('启动监控失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async stopMonitoring() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/model-monitor/stop', {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
this.monitorStatus = data.status;
|
||||||
|
this.updateUI();
|
||||||
|
} else {
|
||||||
|
console.error('停止监控失败:', data.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('停止监控失败:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -20,37 +246,392 @@ class ModelMonitor extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.monitor-container {
|
.monitor-container {
|
||||||
background-color: white;
|
display: flex;
|
||||||
border-radius: 8px;
|
flex-direction: column;
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
gap: 16px;
|
||||||
padding: 16px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monitor-header {
|
.toolbar-section {
|
||||||
display: flex;
|
background-color: white;
|
||||||
justify-content: space-between;
|
border-radius: 8px;
|
||||||
align-items: center;
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
margin-bottom: 20px;
|
padding: 16px;
|
||||||
padding-bottom: 10px;
|
|
||||||
border-bottom: 1px solid #e0e0e0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.monitor-title {
|
.content-container {
|
||||||
font-size: 18px;
|
display: flex;
|
||||||
font-weight: bold;
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-section {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-label {
|
||||||
|
font-size: 14px;
|
||||||
color: #333;
|
color: #333;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.domain-input {
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.domain-input:disabled {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-button:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-button {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-button:hover:not(:disabled) {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop-button {
|
||||||
|
background-color: #f44336;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop-button:hover:not(:disabled) {
|
||||||
|
background-color: #da190b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-display {
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
color: #333;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-table th,
|
||||||
|
.model-table td {
|
||||||
|
padding: 8px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-table th {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-table tr:hover {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#model-chart {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 60px;
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #ccc;
|
||||||
|
transition: .4s;
|
||||||
|
border-radius: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 26px;
|
||||||
|
width: 26px;
|
||||||
|
left: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
background-color: white;
|
||||||
|
transition: .4s;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider {
|
||||||
|
background-color: #2196F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider:before {
|
||||||
|
transform: translateX(26px);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="monitor-container">
|
<div class="monitor-container">
|
||||||
<div class="monitor-header">
|
<div class="toolbar-section">
|
||||||
<div class="monitor-title">模型监控</div>
|
<div class="toolbar">
|
||||||
|
<div class="toolbar-left">
|
||||||
|
<div class="input-group">
|
||||||
|
<label class="input-label">DDS通信域ID:</label>
|
||||||
|
<input type="text" class="domain-input" value="10">
|
||||||
|
</div>
|
||||||
|
<button class="control-button start-button" onclick="this.getRootNode().host.startMonitoring()">开始监控</button>
|
||||||
|
<button class="control-button stop-button" onclick="this.getRootNode().host.stopMonitoring()">停止监控</button>
|
||||||
|
</div>
|
||||||
|
<div class="status-display">监控状态: 未启动</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-container">
|
||||||
|
<div class="panel-section">
|
||||||
|
<h3>模型信息</h3>
|
||||||
|
<table class="model-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>模型名称</th>
|
||||||
|
<th>模型ID</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>线程ID</th>
|
||||||
|
<th>节点</th>
|
||||||
|
<th>优先级</th>
|
||||||
|
<th>运行次数</th>
|
||||||
|
<th>设定频率(Hz)</th>
|
||||||
|
<th>平均频率(Hz)</th>
|
||||||
|
<th>最大频率(Hz)</th>
|
||||||
|
<th>最小频率(Hz)</th>
|
||||||
|
<th>设定周期(ms)</th>
|
||||||
|
<th>平均周期(ms)</th>
|
||||||
|
<th>最大周期(ms)</th>
|
||||||
|
<th>最小周期(ms)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="model-table-body"></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="panel-section">
|
||||||
|
<h3>模型监控</h3>
|
||||||
|
<div class="chart-container">
|
||||||
|
<canvas id="model-chart"></canvas>
|
||||||
|
</div>
|
||||||
|
<div class="chart-controls">
|
||||||
|
<span>显示类型:</span>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="display-type-switch">
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
<span id="display-type-label">频率</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>模型监控组件内容(待实现)</div>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initChart() {
|
||||||
|
const chartElement = this.shadowRoot.querySelector('#model-chart');
|
||||||
|
if (!chartElement) {
|
||||||
|
console.error('找不到图表元素');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保 Chart.js 已加载
|
||||||
|
if (typeof Chart === 'undefined') {
|
||||||
|
console.error('Chart.js 未加载');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 创建图表实例
|
||||||
|
const ctx = chartElement.getContext('2d');
|
||||||
|
if (!ctx) {
|
||||||
|
console.error('无法获取 canvas 上下文');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 销毁已存在的图表实例
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新的图表实例
|
||||||
|
this.chart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: []
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
animation: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: true
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听显示类型切换
|
||||||
|
const switchInput = this.shadowRoot.querySelector('#display-type-switch');
|
||||||
|
const displayTypeLabel = this.shadowRoot.querySelector('#display-type-label');
|
||||||
|
|
||||||
|
if (switchInput && displayTypeLabel) {
|
||||||
|
switchInput.addEventListener('change', (e) => {
|
||||||
|
const isFrequency = e.target.checked;
|
||||||
|
displayTypeLabel.textContent = isFrequency ? '频率' : '周期';
|
||||||
|
this.updateChartDisplayType(isFrequency);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化图表失败:', error);
|
||||||
|
this.chart = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateChartData() {
|
||||||
|
if (!this.chart || !this.modelInfo || !Array.isArray(this.modelInfo)) return;
|
||||||
|
|
||||||
|
const isFrequency = this.shadowRoot.querySelector('#display-type-switch').checked;
|
||||||
|
const currentTime = new Date().toLocaleTimeString();
|
||||||
|
|
||||||
|
// 确保图表数据集存在
|
||||||
|
if (!this.chart.data.datasets || this.chart.data.datasets.length === 0) {
|
||||||
|
this.updateChartDisplayType(isFrequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新数据
|
||||||
|
const newData = {
|
||||||
|
labels: [...this.chart.data.labels, currentTime].slice(-30),
|
||||||
|
datasets: this.modelInfo.map((model, index) => {
|
||||||
|
const dataset = this.chart.data.datasets[index] || {
|
||||||
|
label: model.name,
|
||||||
|
data: [],
|
||||||
|
borderColor: this.getRandomColor(),
|
||||||
|
fill: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const value = isFrequency ? (model.currentFrequency || 0) : (model.currentPeriod || 0);
|
||||||
|
dataset.data = [...dataset.data, value].slice(-30);
|
||||||
|
|
||||||
|
return dataset;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用新数据更新图表
|
||||||
|
this.chart.data = newData;
|
||||||
|
this.chart.update('none'); // 使用 'none' 模式更新,避免触发动画和事件
|
||||||
|
}
|
||||||
|
|
||||||
|
updateChartDisplayType(isFrequency) {
|
||||||
|
if (!this.modelInfo) return;
|
||||||
|
|
||||||
|
const datasets = this.modelInfo.map(model => ({
|
||||||
|
label: model.name,
|
||||||
|
data: [],
|
||||||
|
borderColor: this.getRandomColor(),
|
||||||
|
fill: false
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.chart.data.datasets = datasets;
|
||||||
|
this.chart.update('none'); // 使用 'none' 模式更新
|
||||||
|
}
|
||||||
|
|
||||||
|
getRandomColor() {
|
||||||
|
const letters = '0123456789ABCDEF';
|
||||||
|
let color = '#';
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
color += letters[Math.floor(Math.random() * 16)];
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('model-monitor', ModelMonitor);
|
customElements.define('model-monitor', ModelMonitor);
|
@ -1,6 +1,6 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const { initializeMonitor, cleanupMonitor } = require('../modules/cleanup');
|
const { initializeMonitor, cleanupMonitor } = require('../utils/systemMonitor');
|
||||||
|
|
||||||
// 存储监控服务的状态
|
// 存储监控服务的状态
|
||||||
let monitorStatus = {
|
let monitorStatus = {
|
||||||
|
220
XNSimHtml/routes/ModelMonitor.js
Normal file
220
XNSimHtml/routes/ModelMonitor.js
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { startMonitorModelInfo, stopMonitorModelInfo, getModelInfo } = require('../utils/systemMonitor');
|
||||||
|
|
||||||
|
// 存储监控服务的状态
|
||||||
|
let monitorStatus = {
|
||||||
|
isMonitoring: false,
|
||||||
|
lastError: null
|
||||||
|
};
|
||||||
|
|
||||||
|
// 存储模型频率历史数据
|
||||||
|
const modelFrequencyHistory = new Map(); // Map<modelId, number[]>
|
||||||
|
const MAX_HISTORY_SIZE = 100;
|
||||||
|
|
||||||
|
// 存储模型频率统计
|
||||||
|
const modelFrequencyStats = new Map(); // Map<modelId, {max: number, min: number, avg: number}>
|
||||||
|
|
||||||
|
// 频率转周期(单位:ms)
|
||||||
|
function frequencyToPeriod(frequency) {
|
||||||
|
return frequency === 0 ? 0 : (1000 / frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新模型频率统计
|
||||||
|
function updateModelFrequencyStats(modelId, currentFrequency, setFrequency) {
|
||||||
|
// 初始化历史数据
|
||||||
|
if (!modelFrequencyHistory.has(modelId)) {
|
||||||
|
modelFrequencyHistory.set(modelId, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化统计数据
|
||||||
|
if (!modelFrequencyStats.has(modelId)) {
|
||||||
|
modelFrequencyStats.set(modelId, {
|
||||||
|
max: setFrequency,
|
||||||
|
min: setFrequency,
|
||||||
|
avg: setFrequency
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const history = modelFrequencyHistory.get(modelId);
|
||||||
|
const stats = modelFrequencyStats.get(modelId);
|
||||||
|
|
||||||
|
// 更新最大最小值
|
||||||
|
stats.max = Math.max(stats.max, currentFrequency);
|
||||||
|
stats.min = Math.min(stats.min, currentFrequency);
|
||||||
|
|
||||||
|
// 更新历史数据
|
||||||
|
history.push(currentFrequency);
|
||||||
|
if (history.length > MAX_HISTORY_SIZE) {
|
||||||
|
history.shift(); // 移除最旧的数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算平均值
|
||||||
|
stats.avg = history.reduce((sum, freq) => sum + freq, 0) / history.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动模型监控
|
||||||
|
router.post('/start', async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (monitorStatus.isMonitoring) {
|
||||||
|
return res.status(400).json({ error: '模型监控已经在运行' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = startMonitorModelInfo();
|
||||||
|
if (result && result.includes('失败')) {
|
||||||
|
monitorStatus.lastError = result;
|
||||||
|
return res.status(500).json({ error: result });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空历史数据和统计数据
|
||||||
|
modelFrequencyHistory.clear();
|
||||||
|
modelFrequencyStats.clear();
|
||||||
|
|
||||||
|
monitorStatus.isMonitoring = true;
|
||||||
|
monitorStatus.lastError = null;
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: '模型监控启动成功',
|
||||||
|
status: monitorStatus
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('启动模型监控失败:', error);
|
||||||
|
monitorStatus.lastError = error.message;
|
||||||
|
res.status(500).json({ error: '启动模型监控失败', message: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 停止模型监控
|
||||||
|
router.post('/stop', async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (!monitorStatus.isMonitoring) {
|
||||||
|
return res.status(400).json({ error: '模型监控未在运行' });
|
||||||
|
}
|
||||||
|
|
||||||
|
stopMonitorModelInfo();
|
||||||
|
monitorStatus.isMonitoring = false;
|
||||||
|
monitorStatus.lastError = null;
|
||||||
|
|
||||||
|
// 清空历史数据和统计数据
|
||||||
|
modelFrequencyHistory.clear();
|
||||||
|
modelFrequencyStats.clear();
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: '模型监控停止成功',
|
||||||
|
status: monitorStatus
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('停止模型监控失败:', error);
|
||||||
|
monitorStatus.lastError = error.message;
|
||||||
|
res.status(500).json({ error: '停止模型监控失败', message: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取模型信息
|
||||||
|
router.get('/model-info', async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (!monitorStatus.isMonitoring) {
|
||||||
|
return res.status(400).json({ error: '模型监控未在运行' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = getModelInfo();
|
||||||
|
if (result && result.includes('失败')) {
|
||||||
|
monitorStatus.lastError = result;
|
||||||
|
return res.status(500).json({ error: result });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析JSON字符串
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
data = JSON.parse(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析模型信息JSON失败:', error);
|
||||||
|
return res.status(500).json({ error: '解析模型信息失败', message: error.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查数据结构
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
return res.status(500).json({ error: '模型信息数据格式错误' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查顶层modelStatus字段
|
||||||
|
if (!data.modelStatus || typeof data.modelStatus !== 'object') {
|
||||||
|
return res.status(500).json({ error: '模型状态数据缺失' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造响应数据
|
||||||
|
const responseData = Object.entries(data.modelStatus).map(([modelId, model]) => {
|
||||||
|
// 检查必要字段
|
||||||
|
const requiredFields = [
|
||||||
|
'modelName', 'modelID', 'modelStatus', 'modelThreadID',
|
||||||
|
'modelNode', 'modelPriority', 'modelRunCount',
|
||||||
|
'modelCurrentFrequency', 'modelSetFrequency'
|
||||||
|
];
|
||||||
|
|
||||||
|
const missingFields = requiredFields.filter(field =>
|
||||||
|
model[field] === undefined || model[field] === null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (missingFields.length > 0) {
|
||||||
|
throw new Error(`模型数据不完整: 缺少 ${missingFields.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新频率统计
|
||||||
|
updateModelFrequencyStats(
|
||||||
|
parseInt(modelId),
|
||||||
|
model.modelCurrentFrequency,
|
||||||
|
model.modelSetFrequency
|
||||||
|
);
|
||||||
|
|
||||||
|
const stats = modelFrequencyStats.get(parseInt(modelId));
|
||||||
|
if (!stats) {
|
||||||
|
throw new Error('模型频率统计数据缺失');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算周期值
|
||||||
|
const currentPeriod = frequencyToPeriod(model.modelCurrentFrequency);
|
||||||
|
const setPeriod = frequencyToPeriod(model.modelSetFrequency);
|
||||||
|
const maxPeriod = frequencyToPeriod(stats.min);
|
||||||
|
const minPeriod = frequencyToPeriod(stats.max);
|
||||||
|
const avgPeriod = frequencyToPeriod(stats.avg);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: parseInt(modelId),
|
||||||
|
name: model.modelName,
|
||||||
|
status: model.modelStatus,
|
||||||
|
threadId: model.modelThreadID,
|
||||||
|
node: model.modelNode,
|
||||||
|
priority: model.modelPriority,
|
||||||
|
runCount: model.modelRunCount,
|
||||||
|
// 频率值
|
||||||
|
currentFrequency: model.modelCurrentFrequency,
|
||||||
|
setFrequency: model.modelSetFrequency,
|
||||||
|
maxFrequency: stats.max,
|
||||||
|
minFrequency: stats.min,
|
||||||
|
avgFrequency: stats.avg,
|
||||||
|
// 周期值(单位:ms)
|
||||||
|
currentPeriod: currentPeriod,
|
||||||
|
setPeriod: setPeriod,
|
||||||
|
maxPeriod: maxPeriod,
|
||||||
|
minPeriod: minPeriod,
|
||||||
|
avgPeriod: avgPeriod
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
data: responseData,
|
||||||
|
status: monitorStatus
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取模型信息失败:', error);
|
||||||
|
monitorStatus.lastError = error.message;
|
||||||
|
res.status(500).json({ error: '获取模型信息失败', message: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取监控状态
|
||||||
|
router.get('/status', (req, res) => {
|
||||||
|
res.json(monitorStatus);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
@ -1,6 +1,6 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const { startMonitorSystemInfo, stopMonitorSystemInfo, getSystemInfo, getAllThreadInfo } = require('../modules/cleanup');
|
const { startMonitorSystemInfo, stopMonitorSystemInfo, getSystemInfo, getAllThreadInfo } = require('../utils/systemMonitor');
|
||||||
|
|
||||||
// 存储监控服务的状态
|
// 存储监控服务的状态
|
||||||
let monitorStatus = {
|
let monitorStatus = {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const { loginLib, stringToBuffer } = require('../modules/cleanup');
|
const { loginLib, stringToBuffer } = require('../utils/systemMonitor');
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
// JWT密钥
|
// JWT密钥
|
||||||
|
@ -6,7 +6,7 @@ const cookieParser = require('cookie-parser');
|
|||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
|
|
||||||
// 导入自定义模块
|
// 导入自定义模块
|
||||||
const { performCleanup } = require('./modules/cleanup');
|
const { performCleanup } = require('./utils/systemMonitor');
|
||||||
const authRoutes = require('./routes/auth');
|
const authRoutes = require('./routes/auth');
|
||||||
const versionRoutes = require('./routes/versions');
|
const versionRoutes = require('./routes/versions');
|
||||||
const filesystemRoutes = require('./routes/filesystem');
|
const filesystemRoutes = require('./routes/filesystem');
|
||||||
@ -29,6 +29,7 @@ const userRoutes = require('./routes/users');
|
|||||||
const systemLogRoutes = require('./routes/system-log');
|
const systemLogRoutes = require('./routes/system-log');
|
||||||
const ddsMonitorRoutes = require('./routes/DDSMonitor');
|
const ddsMonitorRoutes = require('./routes/DDSMonitor');
|
||||||
const systemMonitorRoutes = require('./routes/SystemMonitor');
|
const systemMonitorRoutes = require('./routes/SystemMonitor');
|
||||||
|
const modelMonitorRoutes = require('./routes/ModelMonitor');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
@ -99,6 +100,7 @@ app.use('/api', userRoutes);
|
|||||||
app.use('/api/system-log', systemLogRoutes);
|
app.use('/api/system-log', systemLogRoutes);
|
||||||
app.use('/api/dds-monitor', ddsMonitorRoutes);
|
app.use('/api/dds-monitor', ddsMonitorRoutes);
|
||||||
app.use('/api/system-monitor', systemMonitorRoutes);
|
app.use('/api/system-monitor', systemMonitorRoutes);
|
||||||
|
app.use('/api/model-monitor', modelMonitorRoutes);
|
||||||
|
|
||||||
// 接口配置页面路由
|
// 接口配置页面路由
|
||||||
app.get('/interface-config', (req, res) => {
|
app.get('/interface-config', (req, res) => {
|
||||||
|
@ -46,7 +46,10 @@ try {
|
|||||||
'XN_StartMonitorSystemInfo': ['int', [StringType, 'int']],
|
'XN_StartMonitorSystemInfo': ['int', [StringType, 'int']],
|
||||||
'XN_GetSystemInfo': ['int', [StringType, 'int']],
|
'XN_GetSystemInfo': ['int', [StringType, 'int']],
|
||||||
'XN_GetAllThreadInfo': ['int', [StringType, 'int']],
|
'XN_GetAllThreadInfo': ['int', [StringType, 'int']],
|
||||||
'XN_StopMonitorSystemInfo': ['void', []]
|
'XN_StopMonitorSystemInfo': ['void', []],
|
||||||
|
'XN_StartMonitorModelInfo': ['int', [StringType, 'int']],
|
||||||
|
'XN_GetModelInfo': ['int', [StringType, 'int']],
|
||||||
|
'XN_StopMonitorModelInfo': ['void', []]
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`加载 ${monitorLibName} 失败:`, error);
|
console.error(`加载 ${monitorLibName} 失败:`, error);
|
||||||
@ -133,7 +136,7 @@ function getSystemInfo() {
|
|||||||
return '监控服务器库未加载';
|
return '监控服务器库未加载';
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const infoMsg = Buffer.alloc(4096);
|
const infoMsg = Buffer.alloc(8192);
|
||||||
const result = monitorLib.XN_GetSystemInfo(infoMsg, infoMsg.length);
|
const result = monitorLib.XN_GetSystemInfo(infoMsg, infoMsg.length);
|
||||||
|
|
||||||
if (result !== 0) {
|
if (result !== 0) {
|
||||||
@ -151,7 +154,7 @@ function getAllThreadInfo() {
|
|||||||
return '监控服务器库未加载';
|
return '监控服务器库未加载';
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const infoMsg = Buffer.alloc(4096);
|
const infoMsg = Buffer.alloc(8192);
|
||||||
const result = monitorLib.XN_GetAllThreadInfo(infoMsg, infoMsg.length);
|
const result = monitorLib.XN_GetAllThreadInfo(infoMsg, infoMsg.length);
|
||||||
|
|
||||||
if (result !== 0) {
|
if (result !== 0) {
|
||||||
@ -175,6 +178,53 @@ function stopMonitorSystemInfo() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 启动监控模型信息
|
||||||
|
function startMonitorModelInfo() {
|
||||||
|
if (!monitorLib) {
|
||||||
|
return '监控服务器库未加载';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const errorMsg = Buffer.alloc(1024);
|
||||||
|
const result = monitorLib.XN_StartMonitorModelInfo(errorMsg, errorMsg.length);
|
||||||
|
if (result !== 0) {
|
||||||
|
return `启动失败: ${errorMsg.toString('utf8').replace(/\0/g, '')}`;
|
||||||
|
}
|
||||||
|
return '启动成功';
|
||||||
|
} catch (error) {
|
||||||
|
return `启动失败: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取模型信息
|
||||||
|
function getModelInfo() {
|
||||||
|
if (!monitorLib) {
|
||||||
|
return '监控服务器库未加载';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const infoMsg = Buffer.alloc(16384);
|
||||||
|
const result = monitorLib.XN_GetModelInfo(infoMsg, infoMsg.length);
|
||||||
|
|
||||||
|
if (result !== 0) {
|
||||||
|
return `获取失败: ${infoMsg.toString('utf8').replace(/\0/g, '')}`;
|
||||||
|
}
|
||||||
|
return infoMsg.toString('utf8').replace(/\0/g, '');
|
||||||
|
} catch (error) {
|
||||||
|
return `获取失败: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止监控模型信息
|
||||||
|
function stopMonitorModelInfo() {
|
||||||
|
if (!monitorLib) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
monitorLib.XN_StopMonitorModelInfo();
|
||||||
|
} catch (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
loginLib,
|
loginLib,
|
||||||
monitorLib,
|
monitorLib,
|
||||||
@ -185,5 +235,8 @@ module.exports = {
|
|||||||
startMonitorSystemInfo,
|
startMonitorSystemInfo,
|
||||||
getSystemInfo,
|
getSystemInfo,
|
||||||
getAllThreadInfo,
|
getAllThreadInfo,
|
||||||
stopMonitorSystemInfo
|
stopMonitorSystemInfo,
|
||||||
|
startMonitorModelInfo,
|
||||||
|
getModelInfo,
|
||||||
|
stopMonitorModelInfo
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user