diff --git a/graph_viewer.h b/graph_viewer.h
index 879237f..f3db69f 100644
--- a/graph_viewer.h
+++ b/graph_viewer.h
@@ -106,7 +106,7 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
@@ -126,6 +126,7 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
let startTime = 0;
let selectedSignals = [];
let dbcData = {};
+ let lastTimestamps = {}; // 각 신호의 마지막 타임스탬프 추적
const MAX_DATA_POINTS = 60;
const COLORS = [
{line: '#FF6384', fill: 'rgba(255, 99, 132, 0.2)'},
@@ -196,14 +197,14 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
ctx.lineTo(this.width - padding, this.height - padding);
ctx.stroke();
- // Y축 라벨
+ // Y축 레이블
ctx.fillStyle = '#aaa';
ctx.font = '11px Arial';
ctx.textAlign = 'right';
ctx.fillText(maxValue.toFixed(2), padding - 5, padding + 5);
ctx.fillText(minValue.toFixed(2), padding - 5, this.height - padding);
- // X축 라벨
+ // X축 레이블
ctx.textAlign = 'center';
ctx.fillStyle = '#aaa';
ctx.font = '10px Arial';
@@ -229,50 +230,23 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
ctx.stroke();
}
- if (this.data.length < 2) return;
+ if (this.data.length < 1) return;
- // 영역 채우기
- ctx.fillStyle = this.colors.fill;
- ctx.beginPath();
- ctx.moveTo(padding, this.height - padding);
-
- for (let i = 0; i < this.data.length; i++) {
- const x = padding + (graphWidth / (MAX_DATA_POINTS - 1)) * i;
- const y = this.height - padding - ((this.data[i] - minValue) / range) * graphHeight;
- ctx.lineTo(x, y);
- }
-
- ctx.lineTo(padding + (graphWidth / (MAX_DATA_POINTS - 1)) * (this.data.length - 1), this.height - padding);
- ctx.closePath();
- ctx.fill();
-
- // 선
- ctx.strokeStyle = this.colors.line;
- ctx.lineWidth = 2;
- ctx.beginPath();
-
- for (let i = 0; i < this.data.length; i++) {
- const x = padding + (graphWidth / (MAX_DATA_POINTS - 1)) * i;
- const y = this.height - padding - ((this.data[i] - minValue) / range) * graphHeight;
-
- if (i === 0) {
- ctx.moveTo(x, y);
- } else {
- ctx.lineTo(x, y);
- }
- }
-
- ctx.stroke();
-
- // 포인트
+ // 점만 그리기 (선과 영역 채우기 제거)
ctx.fillStyle = this.colors.line;
for (let i = 0; i < this.data.length; i++) {
const x = padding + (graphWidth / (MAX_DATA_POINTS - 1)) * i;
const y = this.height - padding - ((this.data[i] - minValue) / range) * graphHeight;
+ // 점 크기를 5로 증가 (더 잘 보이도록)
ctx.beginPath();
- ctx.arc(x, y, 2, 0, Math.PI * 2);
+ ctx.arc(x, y, 5, 0, Math.PI * 2);
ctx.fill();
+
+ // 점 테두리 추가 (더 선명하게)
+ ctx.strokeStyle = '#fff';
+ ctx.lineWidth = 1;
+ ctx.stroke();
}
}
}
@@ -375,6 +349,7 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
if (!graphing) {
graphing = true;
startTime = Date.now();
+ lastTimestamps = {}; // 타임스탬프 초기화
updateStatus('Graphing...', false);
}
}
@@ -390,16 +365,26 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
messages.forEach(canMsg => {
const idStr = canMsg.id.replace(/\s/g, '').toUpperCase();
const msgId = parseInt(idStr, 16);
+ const timestamp = canMsg.timestamp;
selectedSignals.forEach((signal, index) => {
if (signal.messageId === msgId && charts[index]) {
try {
- const value = decodeSignal(signal, canMsg.data);
- charts[index].addData(value, elapsedTime);
+ // 신호별 고유 키 생성
+ const signalKey = msgId + '_' + signal.name;
- const valueDiv = document.getElementById('value-' + index);
- if (valueDiv) {
- valueDiv.textContent = value.toFixed(2) + (signal.unit ? ' ' + signal.unit : '');
+ // 이전 타임스탬프와 비교 - 새로운 메시지일 때만 점 추가
+ if (!lastTimestamps[signalKey] || lastTimestamps[signalKey] !== timestamp) {
+ const value = decodeSignal(signal, canMsg.data);
+ charts[index].addData(value, elapsedTime);
+
+ // 타임스탬프 업데이트
+ lastTimestamps[signalKey] = timestamp;
+
+ const valueDiv = document.getElementById('value-' + index);
+ if (valueDiv) {
+ valueDiv.textContent = value.toFixed(2) + (signal.unit ? ' ' + signal.unit : '');
+ }
}
} catch(e) {
console.error('Error decoding signal:', e);
diff --git a/index.h b/index.h
index e8eb7b0..8216b6d 100644
--- a/index.h
+++ b/index.h
@@ -275,10 +275,11 @@ const char index_html[] PROGMEM = R"rawliteral(
let canMessages = {};
let messageOrder = [];
- // CAN 속도 이름 매핑
+ // 마지막 업데이트 추적용
+ let lastMessageData = {};
+
const speedNames = ['125 Kbps', '250 Kbps', '500 Kbps', '1 Mbps'];
- // CAN 속도 설정 저장
function saveCanSpeed() {
const speed = document.getElementById('can-speed').value;
try {
@@ -289,7 +290,6 @@ const char index_html[] PROGMEM = R"rawliteral(
}
}
- // CAN 속도 설정 복원
function loadCanSpeed() {
try {
const savedSpeed = localStorage.getItem('canSpeed');
@@ -297,7 +297,6 @@ const char index_html[] PROGMEM = R"rawliteral(
document.getElementById('can-speed').value = savedSpeed;
console.log('Restored CAN speed:', speedNames[savedSpeed]);
- // 복원되었음을 표시
const statusSpan = document.getElementById('speed-status');
if (statusSpan) {
statusSpan.textContent = '(Restored: ' + speedNames[savedSpeed] + ')';
@@ -423,13 +422,24 @@ const char index_html[] PROGMEM = R"rawliteral(
const msg = canMessages[canId];
let row = existingRows.get(canId);
+ // 이전 데이터와 비교하여 실제 변경사항 확인
+ const prevData = lastMessageData[canId];
+ const hasChanged = !prevData ||
+ prevData.data !== msg.data ||
+ prevData.dlc !== msg.dlc ||
+ prevData.timestamp !== msg.timestamp;
+
if (row) {
row.cells[1].textContent = msg.dlc;
row.cells[2].textContent = msg.data;
row.cells[3].textContent = msg.updateCount;
row.cells[4].textContent = msg.timestamp;
- row.classList.add('flash-row');
- setTimeout(() => row.classList.remove('flash-row'), 300);
+
+ // 실제로 변경된 경우에만 flash 효과
+ if (hasChanged) {
+ row.classList.add('flash-row');
+ setTimeout(() => row.classList.remove('flash-row'), 300);
+ }
} else {
row = tbody.insertRow();
row.dataset.canId = canId;
@@ -442,6 +452,14 @@ const char index_html[] PROGMEM = R"rawliteral(
row.classList.add('flash-row');
setTimeout(() => row.classList.remove('flash-row'), 300);
}
+
+ // 현재 데이터 저장
+ lastMessageData[canId] = {
+ data: msg.data,
+ dlc: msg.dlc,
+ timestamp: msg.timestamp,
+ updateCount: msg.updateCount
+ };
});
}
@@ -485,10 +503,8 @@ const char index_html[] PROGMEM = R"rawliteral(
ws.send(JSON.stringify({cmd: 'setSpeed', speed: parseInt(speed)}));
- // 설정 저장
saveCanSpeed();
- // 적용 완료 표시
const statusSpan = document.getElementById('speed-status');
if (statusSpan) {
statusSpan.textContent = '✓ Applied: ' + speedName;
@@ -511,6 +527,7 @@ const char index_html[] PROGMEM = R"rawliteral(
function clearMessages() {
canMessages = {};
messageOrder = [];
+ lastMessageData = {};
document.getElementById('can-messages').innerHTML = '';
}
@@ -518,7 +535,6 @@ const char index_html[] PROGMEM = R"rawliteral(
window.location.href = '/download?file=' + encodeURIComponent(filename);
}
- // 페이지 로드 시 저장된 CAN speed 복원
window.addEventListener('load', function() {
loadCanSpeed();
});