그래프 수신 시만 dot, monitor창 수신시만 표현
This commit is contained in:
@@ -106,7 +106,7 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Real-time CAN Signal Graphs</h1>
|
||||
<h1>Real-time CAN Signal Graphs (Scatter Mode)</h1>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
@@ -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);
|
||||
|
||||
34
index.h
34
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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user