dot 수정 신호 sync
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user