dot 수정 신호 sync

This commit is contained in:
2026-01-24 18:28:04 +00:00
parent b1ed03cf56
commit 9431a100c4

View File

@@ -290,6 +290,7 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
let rangeMode = '10s'; let rangeMode = '10s';
let totalMsgReceived = 0; let totalMsgReceived = 0;
let lastCanCounts = {}; // ★ 각 CAN ID별 마지막 count 저장 let lastCanCounts = {}; // ★ 각 CAN ID별 마지막 count 저장
let lastSignalTimes = {}; // ⭐ 각 신호별 마지막 시간 저장 (time-base 모드용)
const COLORS = [ const COLORS = [
{line: '#FF6384', fill: 'rgba(255, 99, 132, 0.2)'}, {line: '#FF6384', fill: 'rgba(255, 99, 132, 0.2)'},
@@ -360,7 +361,14 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
this.labels.shift(); this.labels.shift();
this.rawValues.shift(); this.rawValues.shift();
} }
}
// ⭐ 배치 데이터 추가 (500ms 동안 받은 모든 데이터)
addDataBatch(values, times) {
values.forEach((value, index) => {
this.addData(value, times[index]);
});
// 배치 추가 후 한 번만 그리기
this.draw(); this.draw();
} }
@@ -379,16 +387,17 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
const graphWidth = this.width - padding * 2; const graphWidth = this.width - padding * 2;
const graphHeight = this.height - padding * 2; const graphHeight = this.height - padding * 2;
ctx.clearRect(0, 0, this.width, this.height); ctx.fillStyle = '#1a1a1a';
ctx.fillRect(0, 0, this.width, this.height);
let displayData = []; let displayData = [];
let displayTimes = []; let displayTimes = [];
let displayLabels = []; let displayLabels = [];
if (rangeMode === '10s') { if (rangeMode === '10s') {
const currentTime = this.times[this.times.length - 1]; const currentTime = parseFloat(this.times[this.times.length - 1]);
for (let i = 0; i < this.times.length; i++) { for (let i = 0; i < this.times.length; i++) {
if (currentTime - this.times[i] <= 10) { if (currentTime - parseFloat(this.times[i]) <= 10) {
displayData.push(this.data[i]); displayData.push(this.data[i]);
displayTimes.push(this.times[i]); displayTimes.push(this.times[i]);
displayLabels.push(this.labels[i]); displayLabels.push(this.labels[i]);
@@ -408,20 +417,20 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
let minTime, maxTime, timeRange; let minTime, maxTime, timeRange;
if (scaleMode === 'time') { if (scaleMode === 'time') {
minTime = displayTimes[0]; minTime = parseFloat(displayTimes[0]);
maxTime = displayTimes[displayTimes.length - 1]; maxTime = parseFloat(displayTimes[displayTimes.length - 1]);
timeRange = maxTime - minTime || 1; timeRange = maxTime - minTime || 1;
} }
ctx.strokeStyle = '#444'; ctx.strokeStyle = '#888';
ctx.lineWidth = 1; ctx.lineWidth = 2;
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(padding, padding); ctx.moveTo(padding, padding);
ctx.lineTo(padding, this.height - padding); ctx.lineTo(padding, this.height - padding);
ctx.lineTo(this.width - padding, this.height - padding); ctx.lineTo(this.width - padding, this.height - padding);
ctx.stroke(); ctx.stroke();
ctx.fillStyle = '#aaa'; ctx.fillStyle = '#ccc';
ctx.font = '11px Arial'; ctx.font = '11px Arial';
ctx.textAlign = 'right'; ctx.textAlign = 'right';
@@ -443,7 +452,7 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
} }
ctx.textAlign = 'center'; ctx.textAlign = 'center';
ctx.fillStyle = '#aaa'; ctx.fillStyle = '#ccc';
ctx.font = '10px Arial'; ctx.font = '10px Arial';
if (displayLabels.length > 0) { if (displayLabels.length > 0) {
ctx.fillText(displayLabels[0] + 's', padding, this.height - padding + 15); ctx.fillText(displayLabels[0] + 's', padding, this.height - padding + 15);
@@ -460,7 +469,7 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
} }
ctx.fillText('Time (sec)', this.width / 2, this.height - 5); ctx.fillText('Time (sec)', this.width / 2, this.height - 5);
ctx.strokeStyle = '#333'; ctx.strokeStyle = '#444';
ctx.lineWidth = 1; ctx.lineWidth = 1;
for (let i = 1; i < 5; i++) { for (let i = 1; i < 5; i++) {
const y = padding + (graphHeight / 5) * i; const y = padding + (graphHeight / 5) * i;
@@ -477,7 +486,7 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
let x; let x;
if (scaleMode === 'time') { if (scaleMode === 'time') {
const timePos = (displayTimes[i] - minTime) / timeRange; const timePos = (parseFloat(displayTimes[i]) - minTime) / timeRange;
x = padding + graphWidth * timePos; x = padding + graphWidth * timePos;
} else { } else {
x = padding + (graphWidth / (MAX_DATA_POINTS - 1)) * i; x = padding + (graphWidth / (MAX_DATA_POINTS - 1)) * i;
@@ -675,11 +684,20 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
// ★★★ 새로운 함수: update 타입 메시지 처리 // ★★★ 새로운 함수: update 타입 메시지 처리
// 서버가 보내는 형식: {id: 숫자, dlc: 숫자, data: [배열], count: 숫자} // 서버가 보내는 형식: {id: 숫자, dlc: 숫자, data: [배열], count: 숫자}
// ★★★ count가 증가한 경우에만 그래프에 데이터 추가 // ★★★ count 차이만큼 배치로 데이터 추가 (500ms 동안 받은 모든 신호)
function processCANDataFromUpdate(messages) { function processCANDataFromUpdate(messages) {
const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(1); const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(1);
const sortedSignals = sortSignalsForDisplay(); const sortedSignals = sortSignalsForDisplay();
// ⭐ 신호별로 데이터를 모을 배치 객체
const signalBatches = {};
sortedSignals.forEach((signal, index) => {
signalBatches[index] = {
values: [],
times: []
};
});
let processedCount = 0; let processedCount = 0;
messages.forEach(canMsg => { messages.forEach(canMsg => {
@@ -694,18 +712,19 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
msgId = msgId & 0x1FFFFFFF; msgId = msgId & 0x1FFFFFFF;
} }
// ★ count가 증가한 경우에만 처리 (새로운 CAN 메시지가 있을 때만) // ★ count 차이 계산 (500ms 동안 몇 개의 메시지가 왔는지)
const prevCount = lastCanCounts[msgId] || 0; const prevCount = lastCanCounts[msgId] || 0;
const currentCount = canMsg.count || 0; const currentCount = canMsg.count || 0;
const countDiff = currentCount - prevCount;
if (currentCount <= prevCount) { if (countDiff <= 0) {
// count가 증가하지 않았으면 스킵 // count가 증가하지 않았으면 스킵
return; return;
} }
// count 업데이트 // count 업데이트
lastCanCounts[msgId] = currentCount; lastCanCounts[msgId] = currentCount;
totalMsgReceived++; totalMsgReceived += countDiff;
// data가 배열로 오는 경우 HEX 문자열로 변환 // data가 배열로 오는 경우 HEX 문자열로 변환
let hexData = ''; let hexData = '';
@@ -720,7 +739,27 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
try { try {
const value = decodeSignalFromHex(signal, hexData); const value = decodeSignalFromHex(signal, hexData);
charts[index].addData(value, elapsedTime); // ⭐ 신호별 시간 간격 계산
const signalKey = `${msgId}_${signal.name}`;
const lastTime = lastSignalTimes[signalKey] || 0;
const currentTime = parseFloat(elapsedTime);
const timeDelta = currentTime - lastTime;
// ⭐ 배치에 데이터 추가 (count 차이만큼, 각각 고유한 시간)
// 예: 10ms 주기 신호가 500ms 동안 50개 왔으면
// countDiff = 50
// 각 데이터를 시간 간격에 맞춰 분산
for (let i = 0; i < countDiff; i++) {
signalBatches[index].values.push(value);
// ⭐ 시간 분산: 500ms 동안 50개면 10ms 간격
const timeOffset = (timeDelta / countDiff) * i;
const dataTime = (lastTime + timeOffset).toFixed(3);
signalBatches[index].times.push(dataTime);
}
// 마지막 시간 업데이트
lastSignalTimes[signalKey] = currentTime;
const valueDiv = document.getElementById('value-' + index); const valueDiv = document.getElementById('value-' + index);
if (valueDiv) { if (valueDiv) {
@@ -731,7 +770,7 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
} }
} }
processedCount++; processedCount += countDiff;
} catch(e) { } catch(e) {
console.error('Error decoding signal ' + signal.name + ':', e); console.error('Error decoding signal ' + signal.name + ':', e);
} }
@@ -739,8 +778,18 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
}); });
}); });
// ⭐ 배치 데이터를 차트에 한 번에 추가
sortedSignals.forEach((signal, index) => {
if (charts[index] && signalBatches[index].values.length > 0) {
charts[index].addDataBatch(
signalBatches[index].values,
signalBatches[index].times
);
}
});
if (processedCount > 0) { if (processedCount > 0) {
console.log('Added', processedCount, 'new data points from update'); console.log('Added', processedCount, 'new data points from update (batch mode)');
} }
updateStatistics(); updateStatistics();