/**
* @class NetworkMonitor
* @description 网络监控组件,用于监控系统网络状态和UDP数据抓包
*/
class NetworkMonitor extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.state = {
ip: '127.0.0.1',
port: 54321,
isMonitoring: false,
data: [],
timer: null,
statusMsg: '',
selectedData: null,
};
}
connectedCallback() {
this.render();
this.initialize();
}
disconnectedCallback() {
if (this.state.timer) {
clearInterval(this.state.timer);
}
}
render() {
this.shadowRoot.innerHTML = `
`;
this.shadowRoot.getElementById('startBtn').onclick = () => this.startMonitor();
this.shadowRoot.getElementById('stopBtn').onclick = () => this.stopMonitor();
this.shadowRoot.getElementById('ip').onchange = (e) => {
this.state.ip = e.target.value;
};
this.shadowRoot.getElementById('port').onchange = (e) => {
this.state.port = parseInt(e.target.value, 10);
};
this.renderDataList();
}
renderDataList() {
const dataList = this.shadowRoot.getElementById('dataList');
if (!dataList) return;
if (!this.state.data.length) {
dataList.innerHTML = '暂无数据
';
return;
}
dataList.innerHTML = this.state.data.map((item, index) => `
时间: ${new Date(item.timestamp).toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
fractionalSecondDigits: 3
})} |
来源: ${item.source} |
大小: ${item.length || this.getDataSize(item.data)} 字节 |
`).join('');
// 添加点击事件
dataList.querySelectorAll('.data-item').forEach(item => {
item.addEventListener('click', (e) => {
const index = parseInt(e.currentTarget.dataset.index);
this.selectDataItem(index);
});
});
}
getDataSize(data) {
// 如果数据是十六进制字符串,每两个字符代表一个字节
if (typeof data === 'string' && /^[0-9a-fA-F]+$/.test(data)) {
return Math.ceil(data.length / 2);
}
return 0;
}
formatDataAsHex(data) {
// 如果数据是十六进制字符串,直接处理
if (typeof data === 'string' && /^[0-9a-fA-F]+$/.test(data)) {
const bytes = [];
for (let i = 0; i < data.length; i += 2) {
const byte = parseInt(data.substr(i, 2), 16);
bytes.push(byte);
}
// 分行显示,每行16个字节
const bytesPerLine = 16;
const lines = [];
for (let i = 0; i < bytes.length; i += bytesPerLine) {
const lineBytes = bytes.slice(i, i + bytesPerLine);
const lineHex = lineBytes.map(byte => byte.toString(16).padStart(2, '0')).join(' ');
const offset = i.toString(16).padStart(4, '0').toUpperCase();
lines.push(`${offset}: ${lineHex}`);
}
return lines.join('\n');
}
return '无效数据格式';
}
setStatus(msg) {
this.state.statusMsg = msg;
this.render();
}
async startMonitor() {
this.setStatus('正在启动UDP抓包...');
try {
const res = await fetch('/api/udp-monitor/start', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ip: this.state.ip, port: this.state.port })
});
const data = await res.json();
if (data.success) {
this.state.isMonitoring = true;
this.setStatus(data.message || 'UDP抓包已启动');
this.startPolling();
} else {
this.state.isMonitoring = false;
this.setStatus(data.error || '启动失败');
}
} catch (e) {
this.state.isMonitoring = false;
this.setStatus('启动UDP抓包失败: ' + e.message);
}
this.render();
}
async stopMonitor() {
this.setStatus('正在停止UDP抓包...');
try {
const res = await fetch('/api/udp-monitor/stop', { method: 'POST' });
const data = await res.json();
this.state.isMonitoring = false;
this.setStatus(data.message || 'UDP抓包已停止');
if (this.state.timer) {
clearInterval(this.state.timer);
this.state.timer = null;
}
} catch (e) {
this.setStatus('停止UDP抓包失败: ' + e.message);
}
this.render();
}
startPolling() {
if (this.state.timer) clearInterval(this.state.timer);
this.state.timer = setInterval(() => this.fetchData(), 1000);
}
async fetchData() {
if (!this.state.isMonitoring) return;
try {
const res = await fetch('/api/udp-monitor/data');
const data = await res.json();
if (data.success) {
if (Array.isArray(data.data) && data.data.length > 0) {
this.state.data = this.state.data.concat(data.data);
// 最多只保留1000条
if (this.state.data.length > 1000) {
this.state.data = this.state.data.slice(-1000);
}
this.renderDataList();
this.updateDetailContent();
}
this.state.isMonitoring = data.isMonitoring;
this.shadowRoot.getElementById('monitorStatus').textContent = data.isMonitoring ? '运行中' : '已停止';
}
} catch (e) {
this.setStatus('获取UDP数据失败: ' + e.message);
}
}
initialize() {
this.setStatus('请设置IP和端口后点击开始抓包');
// 检查当前监控状态
fetch('/api/udp-monitor/status').then(res => res.json()).then(data => {
if (data.success && data.isMonitoring) {
this.state.isMonitoring = true;
this.state.ip = data.ip || this.state.ip;
this.state.port = data.port || this.state.port;
this.setStatus('UDP抓包已在运行');
this.startPolling();
this.render();
}
});
}
reactivate() {
this.initialize();
}
selectDataItem(index) {
this.state.selectedData = index;
this.renderDataList();
this.updateDetailContent();
}
updateDetailContent() {
const detailContent = this.shadowRoot.getElementById('detailContent');
if (!detailContent) return;
if (this.state.selectedData === null || !this.state.data[this.state.selectedData]) {
detailContent.innerHTML = '请从左侧选择一个数据包查看详情
';
return;
}
const item = this.state.data[this.state.selectedData];
detailContent.innerHTML = `
${this.formatHexContent(item.data)}
${this.formatParseContent(item.data)}
`;
}
getPacketIndex(packet) {
// 这里可以根据需要实现数据包序号逻辑
return Math.floor(Math.random() * 1000) + 1;
}
formatHexContent(data) {
// 如果数据是十六进制字符串,直接处理
if (typeof data === 'string' && /^[0-9a-fA-F]+$/.test(data)) {
const bytes = [];
for (let i = 0; i < data.length; i += 2) {
const byte = parseInt(data.substr(i, 2), 16);
bytes.push(byte);
}
// 分行显示,每行16个字节
const bytesPerLine = 16;
const lines = [];
for (let i = 0; i < bytes.length; i += bytesPerLine) {
const lineBytes = bytes.slice(i, i + bytesPerLine);
const lineHex = lineBytes.map(byte => byte.toString(16).padStart(2, '0')).join(' ');
const offset = i.toString(16).padStart(4, '0').toUpperCase();
lines.push(`${offset}: ${lineHex}`);
}
return lines.join('\n');
}
return '无效数据格式';
}
formatParseContent(data) {
if (typeof data !== 'string' || !/^[0-9a-fA-F]+$/.test(data) || data.length < 14) {
return '数据包长度不足,无法解析
';
}
const bytes = [];
for (let i = 0; i < data.length; i += 2) {
const byte = parseInt(data.substr(i, 2), 16);
bytes.push(byte);
}
// 解析数据包头
const header = bytes.slice(0, 6); // 前6个字节
const sizeBytes = bytes.slice(6, 8); // 第7-8个字节表示数据包大小
let parseResult = '数据包头解析
';
// 1. a6-XNSim数据
parseResult += `1. ${header[0].toString(16).padStart(2, '0')} - XNSim数据
`;
// 2. c0-C909数据 c1-C919数据
const dataType = header[1];
let dataTypeDesc = '';
if (dataType === 0xc0) {
dataTypeDesc = 'C909数据';
} else if (dataType === 0xc1) {
dataTypeDesc = 'C919数据';
} else {
dataTypeDesc = `未知数据类型(${dataType.toString(16).padStart(2, '0')})`;
}
parseResult += `2. ${dataType.toString(16).padStart(2, '0')} - ${dataTypeDesc}
`;
// 3. ATA章节号
const ataChapter = header[2];
parseResult += `3. ${ataChapter.toString(16).padStart(2, '0')} - ATA章节号 (ATA${ataChapter.toString().padStart(2, '0')})
`;
// 4. 模型编号
const modelNumber = header[3];
parseResult += `4. ${modelNumber.toString(16).padStart(2, '0')} - 模型编号 (${modelNumber})
`;
// 5. 结构体类别
const structType = header[4];
let structTypeDesc = '';
switch (structType) {
case 0x00:
structTypeDesc = '输入结构体';
break;
case 0x01:
structTypeDesc = '输出结构体';
break;
case 0x02:
structTypeDesc = '心跳结构体';
break;
default:
structTypeDesc = `未知结构体类型(${structType.toString(16).padStart(2, '0')})`;
}
parseResult += `5. ${structType.toString(16).padStart(2, '0')} - 结构体类别 (${structTypeDesc})
`;
// 6. 数据传输方向
const direction = header[5];
let directionDesc = '';
switch (direction) {
case 0x00:
directionDesc = '输入';
break;
case 0x01:
directionDesc = '输出';
break;
default:
directionDesc = `未知方向(${direction.toString(16).padStart(2, '0')})`;
}
parseResult += `6. ${direction.toString(16).padStart(2, '0')} - 数据传输方向 (${directionDesc})
`;
// 7. 数据包大小
const packetSize = (sizeBytes[0] << 8) | sizeBytes[1];
parseResult += `7. ${sizeBytes[0].toString(16).padStart(2, '0')} ${sizeBytes[1].toString(16).padStart(2, '0')} - 数据包大小 (${packetSize} 字节)
`;
return parseResult;
}
}
customElements.define('network-monitor', NetworkMonitor);