class ModelMonitor extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.monitorStatus = {
isMonitoring: false,
lastError: null
};
this.modelInfo = null;
this.statusCheckInterval = null;
this.chart = null;
this.chartInitialized = false;
}
connectedCallback() {
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 `
${model.name || '未知'} |
${model.id || '未知'} |
${statusInfo.text} |
${model.threadId || '未知'} |
${model.node} |
${model.priority || '未知'} |
${model.runCount || '0'} |
${(model.setFrequency || 0).toFixed(2)} |
${(model.avgFrequency || 0).toFixed(2)} |
${(model.maxFrequency || 0).toFixed(2)} |
${(model.minFrequency || 0).toFixed(2)} |
${(model.setPeriod || 0).toFixed(2)} |
${(model.avgPeriod || 0).toFixed(2)} |
${(model.maxPeriod || 0).toFixed(2)} |
${(model.minPeriod || 0).toFixed(2)} |
`;
}).join('');
// 更新图表数据
this.updateChartData();
} else {
modelTableBody.innerHTML = '暂无模型信息 |
';
// 清空图表数据
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() {
this.shadowRoot.innerHTML = `
模型信息
模型名称 |
模型ID |
状态 |
线程ID |
节点 |
优先级 |
运行次数 |
设定频率(Hz) |
平均频率(Hz) |
最大频率(Hz) |
最小频率(Hz) |
设定周期(ms) |
平均周期(ms) |
最大周期(ms) |
最小周期(ms) |
`;
}
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);