class ModelMonitor extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this.monitorId = `model_monitor_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; this.monitorStatus = { isMonitoring: false, lastError: null }; this.modelInfo = null; this.statusCheckInterval = null; this.chart = null; this.chartInitialized = false; this.domainId = '10'; // 默认值 this.isActive = false; } connectedCallback() { this.render(); this.isActive = true; // 设置初始状态为激活 // 等待组件完全加载 setTimeout(() => { this.initializeComponent(); }, 100); } async initializeComponent() { try { // 确保图表被正确初始化 if (this.chart) { this.chart.destroy(); this.chart = null; } // 初始化图表 this.initChart(); // 检查当前监控状态 const statusResponse = await fetch('/api/model-monitor/status'); const statusData = await statusResponse.json(); this.monitorStatus = statusData; // 如果已经在监控中,立即开始获取数据 if (this.monitorStatus.isMonitoring) { // 先获取一次数据 await this.checkMonitorStatus(); // 然后开始定时检查 this.startStatusCheck(); } this.chartInitialized = true; } catch (error) { console.error('初始化组件失败:', error); this.monitorStatus.lastError = error.message; this.updateUI(); } } // 修改 reactivate 方法 reactivate() { this.isActive = true; // 如果已经在监控中,重新开始状态检查 if (this.monitorStatus.isMonitoring) { this.startStatusCheck(); } } // 修改 startStatusCheck 方法 startStatusCheck() { if (!this.isActive) return; // 如果已经有定时器在运行,先停止它 this.stopStatusCheck(); let consecutiveErrors = 0; const maxConsecutiveErrors = 3; const checkStatus = async () => { if (!this.isActive) return; try { await this.checkMonitorStatus(); consecutiveErrors = 0; // 重置错误计数 } catch (error) { consecutiveErrors++; console.error(`状态检查失败 (${consecutiveErrors}/${maxConsecutiveErrors}):`, error); if (consecutiveErrors >= maxConsecutiveErrors) { console.error('连续错误次数过多,停止状态检查'); this.stopStatusCheck(); this.monitorStatus.lastError = '监控服务异常,请重新启动监控'; this.updateUI(); return; } } }; // 立即执行一次 checkStatus(); // 设置定时器,每秒执行一次 this.statusCheckInterval = setInterval(checkStatus, 1000); } // 修改 stopStatusCheck 方法 stopStatusCheck() { if (this.statusCheckInterval) { clearInterval(this.statusCheckInterval); this.statusCheckInterval = null; } } // 修改 startMonitoring 方法 async startMonitoring() { const savedSelection = localStorage.getItem('xnsim-selection'); const selection = savedSelection ? JSON.parse(savedSelection) : {}; const confID = selection.configurationId; try { // 获取构型参数 const configResponse = await fetch(`/api/configurations/${confID}`); if (!configResponse.ok) { throw new Error('获取构型参数失败'); } const configData = await configResponse.json(); // 从构型参数中提取域ID const domainId = configData.DomainID; if (!domainId) { throw new Error('构型参数中未找到有效的域ID'); } this.domainId = domainId; // 首先检查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, monitorId: this.monitorId }) }); 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(); this.startStatusCheck(); // 直接启动状态检查,不需要检查 isActive } else { console.error('启动监控失败:', data.error); } } catch (error) { console.error('启动监控失败:', error); } } disconnectedCallback() { this.isActive = false; this.stopStatusCheck(); // 注销监控器 fetch('/api/dds-monitor/unregister', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ monitorId: this.monitorId }) }).catch(error => { console.error('注销监控器失败:', error); }); // 不要在这里销毁图表实例,让它在重新激活时重新创建 } 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'); if (!modelResponse.ok) { throw new Error(`模型信息获取失败: ${modelResponse.status}`); } const modelData = await modelResponse.json(); if (!modelResponse.ok) { console.error('获取模型信息失败:', modelData.error || modelData.message); this.monitorStatus.lastError = modelData.error || modelData.message; // 保持原有数据不变 } else { // 只有在数据发生变化时才更新 if (JSON.stringify(this.modelInfo) !== JSON.stringify(modelData.data)) { this.modelInfo = modelData.data; this.monitorStatus.lastError = null; // 确保图表存在 if (!this.chart) { this.initChart(); } // 更新UI this.updateUI(); } } } catch (error) { console.warn('获取模型信息失败:', error); // 保持原有数据不变 } } else { // 只有在状态发生变化时才更新 if (this.modelInfo !== null) { this.modelInfo = null; this.monitorStatus.lastError = null; this.updateUI(); } } } catch (error) { console.error('获取监控状态失败:', error); this.monitorStatus.lastError = error.message; 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 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) { startButton.disabled = true; stopButton.disabled = false; } else { 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 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 方法 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 = null; } // 创建新的图表实例 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);