添加了数组数据监控的图表绘制支持
This commit is contained in:
parent
6f46cff40e
commit
b5f41c40b8
Binary file not shown.
@ -198,10 +198,6 @@ class FloatingChartWindow extends HTMLElement {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.window-content.minimized {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
@ -240,7 +236,6 @@ class FloatingChartWindow extends HTMLElement {
|
||||
<div class="window-header">
|
||||
<span class="window-title">${this.getAttribute('title') || '图表窗口'}</span>
|
||||
<div class="window-controls">
|
||||
<button class="window-control-button minimize-button">−</button>
|
||||
<button class="window-control-button close-button">×</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -364,6 +359,16 @@ class FloatingChartWindow extends HTMLElement {
|
||||
}
|
||||
};
|
||||
|
||||
// 确保数据集配置正确
|
||||
if (chartData.datasets && chartData.datasets.length > 0) {
|
||||
chartData.datasets = chartData.datasets.map(dataset => ({
|
||||
...dataset,
|
||||
borderWidth: 2,
|
||||
pointRadius: 0,
|
||||
tension: 0
|
||||
}));
|
||||
}
|
||||
|
||||
this.chartInstance = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: chartData,
|
||||
@ -373,12 +378,10 @@ class FloatingChartWindow extends HTMLElement {
|
||||
|
||||
addEventListeners() {
|
||||
const header = this.shadowRoot.querySelector('.window-header');
|
||||
const minimizeButton = this.shadowRoot.querySelector('.minimize-button');
|
||||
const closeButton = this.shadowRoot.querySelector('.close-button');
|
||||
const resizeHandle = this.shadowRoot.querySelector('.resize-handle');
|
||||
|
||||
header.addEventListener('mousedown', this.handleMouseDown);
|
||||
minimizeButton.addEventListener('click', this.handleMinimize);
|
||||
closeButton.addEventListener('click', this.handleClose);
|
||||
resizeHandle.addEventListener('mousedown', this.handleResizeStart);
|
||||
|
||||
@ -394,7 +397,7 @@ class FloatingChartWindow extends HTMLElement {
|
||||
handleMouseDown(e) {
|
||||
// 检查点击是否在标题栏或其子元素上
|
||||
const header = this.shadowRoot.querySelector('.window-header');
|
||||
if (header && (e.target === header || header.contains(e.target)) && !this.isMinimized) {
|
||||
if (header && (e.target === header || header.contains(e.target))) {
|
||||
this.isDragging = true;
|
||||
this.dragOffset = {
|
||||
x: e.clientX - this.position.x,
|
||||
@ -420,7 +423,7 @@ class FloatingChartWindow extends HTMLElement {
|
||||
y: Math.max(minY, e.clientY - this.dragOffset.y)
|
||||
};
|
||||
this.updatePosition();
|
||||
} else if (this.isResizing && !this.isMinimized) {
|
||||
} else if (this.isResizing) {
|
||||
const deltaX = e.clientX - this.resizeStart.x;
|
||||
const deltaY = e.clientY - this.resizeStart.y;
|
||||
|
||||
@ -452,7 +455,7 @@ class FloatingChartWindow extends HTMLElement {
|
||||
}
|
||||
|
||||
handleResize(e) {
|
||||
if (this.isResizing && !this.isMinimized) {
|
||||
if (this.isResizing) {
|
||||
const deltaX = e.clientX - this.resizeStart.x;
|
||||
const deltaY = e.clientY - this.resizeStart.y;
|
||||
|
||||
@ -546,6 +549,38 @@ class FloatingChartWindow extends HTMLElement {
|
||||
// 始终使用普通数字格式,保留一位小数
|
||||
return `${context.dataset.label}: ${value.toFixed(1)}`;
|
||||
};
|
||||
|
||||
// 计算所有数据点的范围
|
||||
let min = Infinity;
|
||||
let max = -Infinity;
|
||||
|
||||
data.datasets.forEach(dataset => {
|
||||
if (dataset.data && dataset.data.length > 0) {
|
||||
const datasetMin = Math.min(...dataset.data);
|
||||
const datasetMax = Math.max(...dataset.data);
|
||||
min = Math.min(min, datasetMin);
|
||||
max = Math.max(max, datasetMax);
|
||||
}
|
||||
});
|
||||
|
||||
if (min !== Infinity && max !== -Infinity) {
|
||||
const range = max - min;
|
||||
// 如果所有数据都是0,使用固定范围
|
||||
if (min === 0 && max === 0) {
|
||||
options.scales.y.min = -1;
|
||||
options.scales.y.max = 1;
|
||||
}
|
||||
// 如果范围很小,使用固定比例
|
||||
else if (range < 1) {
|
||||
options.scales.y.min = min - 0.5;
|
||||
options.scales.y.max = max + 0.5;
|
||||
} else {
|
||||
// 如果范围较大,使用百分比
|
||||
const margin = range * 0.2; // 使用20%的边距
|
||||
options.scales.y.min = min - margin;
|
||||
options.scales.y.max = max + margin;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新图表
|
||||
this.chartInstance.update('none');
|
||||
@ -565,6 +600,70 @@ class FloatingChartWindow extends HTMLElement {
|
||||
window.style.zIndex = zIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加新方法:处理数据更新
|
||||
handleDataUpdate(values, interfaceName) {
|
||||
if (!this.chartInstance) return;
|
||||
|
||||
const chartData = this.chartInstance.data;
|
||||
|
||||
// 如果是第一次收到数据,创建数据集
|
||||
if (chartData.datasets.length === 0) {
|
||||
if (values.length > 1) {
|
||||
// 创建多个数据集
|
||||
chartData.datasets = values.map((_, index) => ({
|
||||
label: `${interfaceName} (${index + 1})`,
|
||||
data: [],
|
||||
borderColor: this.getRandomColor(),
|
||||
fill: false,
|
||||
borderWidth: 2,
|
||||
pointRadius: 0,
|
||||
tension: 0
|
||||
}));
|
||||
} else {
|
||||
// 创建单个数据集
|
||||
chartData.datasets = [{
|
||||
label: interfaceName,
|
||||
data: [],
|
||||
borderColor: this.getRandomColor(),
|
||||
fill: false,
|
||||
borderWidth: 2,
|
||||
pointRadius: 0,
|
||||
tension: 0
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// 添加新的数据点
|
||||
chartData.labels.push(this.dataPointIndex.toString());
|
||||
values.forEach((value, index) => {
|
||||
if (index < chartData.datasets.length) {
|
||||
chartData.datasets[index].data.push(value);
|
||||
}
|
||||
});
|
||||
this.dataPointIndex++;
|
||||
|
||||
// 保持最近100个数据点
|
||||
if (chartData.labels.length > 100) {
|
||||
chartData.labels.shift();
|
||||
chartData.datasets.forEach(dataset => {
|
||||
dataset.data.shift();
|
||||
});
|
||||
}
|
||||
|
||||
// 更新图表
|
||||
this.updateChartData(chartData);
|
||||
}
|
||||
|
||||
// 添加新方法:获取随机颜色
|
||||
getRandomColor() {
|
||||
const letters = '0123456789ABCDEF';
|
||||
let color = '#';
|
||||
for (let i = 0; i < 6; i++) {
|
||||
color += letters[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('floating-chart-window', FloatingChartWindow);
|
@ -366,6 +366,7 @@ class DataMonitor extends HTMLElement {
|
||||
}
|
||||
|
||||
const groupedInterfaces = this.getGroupedInterfaces();
|
||||
const allData = {}; // 存储所有结构体的数据
|
||||
|
||||
// 对每个结构体启动监控并获取数据
|
||||
for (const [structName, interfaceNames] of Object.entries(groupedInterfaces)) {
|
||||
@ -403,13 +404,18 @@ class DataMonitor extends HTMLElement {
|
||||
throw new Error(`获取监控数据失败: 返回数据为空`);
|
||||
}
|
||||
|
||||
// 更新表格数据
|
||||
this.updateTableData(responseData.data);
|
||||
// 合并数据
|
||||
Object.assign(allData, responseData.data);
|
||||
} catch (structError) {
|
||||
console.error(`处理结构体 ${structName} 时出错:`, structError);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 一次性更新所有数据
|
||||
if (Object.keys(allData).length > 0) {
|
||||
this.updateTableData(allData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('数据更新失败:', error);
|
||||
this.stopDataUpdateTimer();
|
||||
@ -488,7 +494,12 @@ class DataMonitor extends HTMLElement {
|
||||
* @param {string} modelStructName - 结构体名称
|
||||
*/
|
||||
async handlePlot(interfaceName, modelStructName) {
|
||||
|
||||
// 检查监控状态
|
||||
const statusIndicator = this.shadowRoot.getElementById('statusIndicator');
|
||||
if (!statusIndicator || !statusIndicator.classList.contains('active')) {
|
||||
return; // 如果不在监控状态,直接返回
|
||||
}
|
||||
|
||||
// 检查是否已经存在该接口的图表窗口
|
||||
const windowId = `${interfaceName}_${modelStructName}`;
|
||||
if (this.chartWindows.has(windowId)) {
|
||||
@ -506,12 +517,7 @@ class DataMonitor extends HTMLElement {
|
||||
// 创建图表数据
|
||||
const chartData = {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: interfaceName,
|
||||
data: [],
|
||||
borderColor: this.getRandomColor(),
|
||||
fill: false
|
||||
}]
|
||||
datasets: []
|
||||
};
|
||||
|
||||
// 创建图表配置
|
||||
@ -530,6 +536,7 @@ class DataMonitor extends HTMLElement {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: false,
|
||||
display: true,
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
// 如果数值的绝对值大于1000或小于0.1,使用科学计数法
|
||||
@ -567,11 +574,7 @@ class DataMonitor extends HTMLElement {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
const value = context.raw;
|
||||
// 如果数值的绝对值大于1000或小于0.1,使用科学计数法
|
||||
if (Math.abs(value) > 1000 || (Math.abs(value) < 0.1 && value !== 0)) {
|
||||
return `${context.dataset.label}: ${value.toExponential(1)}`;
|
||||
}
|
||||
// 否则使用普通数字,保留一位小数
|
||||
// 始终使用普通数字格式,保留一位小数
|
||||
return `${context.dataset.label}: ${value.toFixed(1)}`;
|
||||
}
|
||||
}
|
||||
@ -594,7 +597,7 @@ class DataMonitor extends HTMLElement {
|
||||
|
||||
// 再设置属性
|
||||
floatingWindow.setAttribute('title', interfaceName);
|
||||
floatingWindow.setAttribute('initial-position', JSON.stringify({ x: 400, y: 200 })); // 调整初始位置
|
||||
floatingWindow.setAttribute('initial-position', JSON.stringify({ x: 400, y: 200 }));
|
||||
floatingWindow.setAttribute('initial-size', JSON.stringify({ width: 400, height: 300 }));
|
||||
floatingWindow.setAttribute('chart-data', JSON.stringify(chartData));
|
||||
floatingWindow.setAttribute('chart-options', JSON.stringify(chartOptions));
|
||||
@ -608,42 +611,26 @@ class DataMonitor extends HTMLElement {
|
||||
|
||||
if (row && row.monitorData) {
|
||||
try {
|
||||
const data = JSON.parse(row.monitorData);
|
||||
let value;
|
||||
// 处理数值类型
|
||||
if (typeof data === 'number') {
|
||||
value = data;
|
||||
} else if (typeof data === 'object') {
|
||||
value = JSON.stringify(data);
|
||||
const data = row.monitorData;
|
||||
let values = [];
|
||||
|
||||
// 尝试解析数据
|
||||
if (typeof data === 'string' && data.includes(',')) {
|
||||
// 如果是逗号分隔的字符串,分割并转换为数字
|
||||
values = data.split(',').map(v => parseFloat(v.trim()));
|
||||
} else if (typeof data === 'number') {
|
||||
// 如果是单个数字
|
||||
values = [data];
|
||||
} else {
|
||||
// 尝试将字符串转换为数字
|
||||
const numValue = parseFloat(data);
|
||||
value = isNaN(numValue) ? data : numValue;
|
||||
}
|
||||
|
||||
// 使用独立的数据点计数器
|
||||
chartData.labels.push(floatingWindow.dataPointIndex.toString());
|
||||
chartData.datasets[0].data.push(value);
|
||||
floatingWindow.dataPointIndex++; // 增加计数器
|
||||
|
||||
// 保持最近100个数据点
|
||||
if (chartData.labels.length > 100) {
|
||||
chartData.labels.shift();
|
||||
chartData.datasets[0].data.shift();
|
||||
values = isNaN(numValue) ? [] : [numValue];
|
||||
}
|
||||
|
||||
// 如果是第一个数据点,设置y轴范围
|
||||
if (chartData.datasets[0].data.length === 1 && typeof value === 'number') {
|
||||
// 计算合适的y轴范围
|
||||
const range = Math.abs(value) * 0.2; // 使用20%的范围
|
||||
chartOptions.scales.y.min = value - range;
|
||||
chartOptions.scales.y.max = value + range;
|
||||
// 更新图表配置
|
||||
floatingWindow.setAttribute('chart-options', JSON.stringify(chartOptions));
|
||||
// 如果数据有效,更新图表
|
||||
if (values.length > 0) {
|
||||
floatingWindow.handleDataUpdate(values, interfaceName);
|
||||
}
|
||||
|
||||
// 更新图表数据
|
||||
floatingWindow.setAttribute('chart-data', JSON.stringify(chartData));
|
||||
} catch (e) {
|
||||
console.error('解析数据失败:', e);
|
||||
}
|
||||
@ -671,6 +658,10 @@ class DataMonitor extends HTMLElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
// 获取当前监控状态
|
||||
const statusIndicator = this.shadowRoot.getElementById('statusIndicator');
|
||||
const isMonitoring = statusIndicator && statusIndicator.classList.contains('active');
|
||||
|
||||
// 按ModelStructName分组
|
||||
const groupedInterfaces = this.filteredInterfaces.reduce((groups, item) => {
|
||||
const group = groups[item.ModelStructName] || [];
|
||||
@ -1043,7 +1034,8 @@ class DataMonitor extends HTMLElement {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${this.tableData.map(row => ` <tr>
|
||||
${this.tableData.map(row => `
|
||||
<tr>
|
||||
<td title="${row.InterfaceName}">${row.InterfaceName}</td>
|
||||
<td title="${row.ModelStructName}">${row.ModelStructName}</td>
|
||||
<td class="data-cell" title="${this.formatMonitorData(row.monitorData)}">${this.formatMonitorData(row.monitorData)}</td>
|
||||
|
Loading…
x
Reference in New Issue
Block a user