can dbc graph Value Table 파싱

This commit is contained in:
2025-10-21 10:57:07 +00:00
parent 54230dd627
commit c70bc58b34
2 changed files with 128 additions and 10 deletions

57
graph.h
View File

@@ -375,11 +375,41 @@ const char graph_html[] PROGMEM = R"rawliteral(
}
function parseDBCContent(content) {
dbcData = {messages: {}};
dbcData = {messages: {}, valueTables: {}};
allSignals = [];
const lines = content.split('\n');
let currentMessage = null;
// 먼저 VAL_ 정의를 파싱
for (let line of lines) {
line = line.trim();
if (line.startsWith('VAL_ ')) {
// VAL_ 형식: VAL_ <MessageID> <SignalName> <value> "<text>" ...;
const match = line.match(/VAL_\s+(\d+)\s+(\w+)\s+(.+);/);
if (match) {
const msgId = parseInt(match[1]);
const sigName = match[2];
const valuesStr = match[3];
// Extended CAN ID 처리
const normalizedMsgId = (msgId & 0x80000000) ? (msgId & 0x1FFFFFFF) : msgId;
const key = normalizedMsgId + '_' + sigName;
dbcData.valueTables[key] = {};
// 값-텍스트 쌍 파싱
const valueMatches = valuesStr.matchAll(/(\d+)\s+"([^"]+)"/g);
for (let vm of valueMatches) {
dbcData.valueTables[key][parseInt(vm[1])] = vm[2];
}
console.log('Parsed Value Table: ' + key, dbcData.valueTables[key]);
}
}
}
// 메시지와 시그널 파싱
for (let line of lines) {
line = line.trim();
@@ -390,10 +420,8 @@ const char graph_html[] PROGMEM = R"rawliteral(
const name = match[2];
const dlc = parseInt(match[3]);
// Extended CAN ID 처리 (bit 31이 set되어 있으면 제거)
// CAN Extended format에서 bit 31은 Extended ID 플래그
if (id & 0x80000000) {
id = id & 0x1FFFFFFF; // bit 31 제거, 29-bit ID만 사용
id = id & 0x1FFFFFFF;
}
currentMessage = {
@@ -410,8 +438,9 @@ const char graph_html[] PROGMEM = R"rawliteral(
else if (line.startsWith('SG_ ') && currentMessage) {
const match = line.match(/SG_\s+(\w+)\s*:\s*(\d+)\|(\d+)@([01])([+-])\s*\(([^,]+),([^)]+)\)\s*\[([^\]]+)\]\s*"([^"]*)"/);
if (match) {
const signalName = match[1];
const signal = {
name: match[1],
name: signalName,
startBit: parseInt(match[2]),
bitLength: parseInt(match[3]),
byteOrder: match[4] === '0' ? 'motorola' : 'intel',
@@ -424,6 +453,14 @@ const char graph_html[] PROGMEM = R"rawliteral(
messageName: currentMessage.name,
messageDlc: currentMessage.dlc
};
// Value Table 연결
const vtKey = currentMessage.id + '_' + signalName;
if (dbcData.valueTables[vtKey]) {
signal.valueTable = dbcData.valueTables[vtKey];
console.log(' Signal with value table: ' + signalName, signal.valueTable);
}
currentMessage.signals.push(signal);
allSignals.push(signal);
@@ -672,7 +709,14 @@ const char graph_html[] PROGMEM = R"rawliteral(
console.log('=== DBC Debug Info ===');
console.log('Total Messages: ' + Object.keys(dbcData.messages).length);
console.log('Total Signals: ' + allSignals.length);
console.log('Total Value Tables: ' + Object.keys(dbcData.valueTables).length);
console.log('\n=== Value Tables ===');
for (let key in dbcData.valueTables) {
console.log(key + ':', dbcData.valueTables[key]);
}
console.log('\n=== Messages ===');
Object.values(dbcData.messages).forEach(msg => {
console.log('\nMessage: ' + msg.name);
console.log(' ID: ' + msg.id + ' (0x' + msg.id.toString(16).toUpperCase() + ')');
@@ -681,6 +725,9 @@ const char graph_html[] PROGMEM = R"rawliteral(
msg.signals.forEach(sig => {
console.log(' - ' + sig.name + ': ' + sig.startBit + '|' + sig.bitLength + ' [' + sig.unit + ']');
if (sig.valueTable) {
console.log(' Value Table:', sig.valueTable);
}
});
});

View File

@@ -211,8 +211,10 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
this.data = [];
this.times = [];
this.labels = [];
this.rawValues = []; // 원본 숫자값 저장
this.colors = COLORS[colorIndex % COLORS.length];
this.currentValue = 0;
this.currentText = ''; // 현재 텍스트 값
this.resizeCanvas();
window.addEventListener('resize', () => this.resizeCanvas());
@@ -232,17 +234,33 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
this.data.push(value);
this.times.push(parseFloat(time));
this.labels.push(time);
this.rawValues.push(value);
this.currentValue = value;
// Value Table이 있으면 텍스트 값 저장
if (this.signal.valueTable && this.signal.valueTable[value] !== undefined) {
this.currentText = this.signal.valueTable[value];
} else {
this.currentText = '';
}
if (scaleMode === 'index' && this.data.length > MAX_DATA_POINTS) {
this.data.shift();
this.times.shift();
this.labels.shift();
this.rawValues.shift();
}
this.draw();
}
getValueText(value) {
if (this.signal.valueTable && this.signal.valueTable[value] !== undefined) {
return this.signal.valueTable[value];
}
return value.toFixed(2);
}
draw() {
if (this.data.length === 0) return;
@@ -285,6 +303,7 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
timeRange = maxTime - minTime || 1;
}
// 축 그리기
ctx.strokeStyle = '#444';
ctx.lineWidth = 1;
ctx.beginPath();
@@ -293,12 +312,31 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
ctx.lineTo(this.width - padding, this.height - padding);
ctx.stroke();
// Y축 레이블 (Value Table이 있으면 텍스트로 표시)
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);
if (this.signal.valueTable) {
// Value Table이 있는 경우
const uniqueValues = [...new Set(displayData)].sort((a, b) => a - b);
if (uniqueValues.length <= 5) {
uniqueValues.forEach((val, idx) => {
const y = this.height - padding - ((val - minValue) / range) * graphHeight;
const text = this.getValueText(val);
ctx.fillText(text, padding - 5, y + 5);
});
} else {
ctx.fillText(this.getValueText(maxValue), padding - 5, padding + 5);
ctx.fillText(this.getValueText(minValue), padding - 5, this.height - padding);
}
} else {
// 일반 숫자 표시
ctx.fillText(maxValue.toFixed(2), padding - 5, padding + 5);
ctx.fillText(minValue.toFixed(2), padding - 5, this.height - padding);
}
// X축 레이블
ctx.textAlign = 'center';
ctx.fillStyle = '#aaa';
ctx.font = '10px Arial';
@@ -317,6 +355,7 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
}
ctx.fillText('Time (sec)', this.width / 2, this.height - 5);
// 그리드 라인
ctx.strokeStyle = '#333';
ctx.lineWidth = 1;
for (let i = 1; i < 5; i++) {
@@ -327,6 +366,7 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
ctx.stroke();
}
// 데이터 포인트 그리기
if (displayData.length < 1) return;
ctx.fillStyle = this.colors.line;
@@ -417,6 +457,14 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
});
console.log('Loaded', selectedSignals.length, 'signals');
// Value Table 정보 로깅
selectedSignals.forEach(sig => {
if (sig.valueTable) {
console.log('Signal with Value Table:', sig.name, sig.valueTable);
}
});
return true;
} catch(e) {
console.error('Failed to load data:', e);
@@ -539,12 +587,17 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
try {
const value = decodeSignal(signal, canMsg.data);
// 항상 추가 (중복 체크 제거)
// 항상 추가
charts[index].addData(value, elapsedTime);
const valueDiv = document.getElementById('value-' + index);
if (valueDiv) {
valueDiv.textContent = value.toFixed(2) + (signal.unit ? ' ' + signal.unit : '');
// Value Table이 있으면 텍스트로 표시
if (signal.valueTable && signal.valueTable[value] !== undefined) {
valueDiv.textContent = signal.valueTable[value];
} else {
valueDiv.textContent = value.toFixed(2) + (signal.unit ? ' ' + signal.unit : '');
}
}
processedCount++;
@@ -633,6 +686,11 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
sortedSignals.forEach(signal => {
const unit = signal.unit ? ' [' + signal.unit + ']' : '';
csvContent += ',' + signal.name + unit;
// Value Table이 있으면 텍스트 컬럼도 추가
if (signal.valueTable) {
csvContent += ',' + signal.name + '_Text';
}
});
csvContent += '\n';
@@ -654,9 +712,20 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
if (timeValue === '') {
timeValue = chart.labels[i];
}
row += ',' + chart.data[i].toFixed(6);
const value = chart.data[i];
row += ',' + value.toFixed(6);
// Value Table이 있으면 텍스트도 추가
if (signal.valueTable) {
const text = signal.valueTable[value] !== undefined ?
signal.valueTable[value] : '';
row += ',"' + text + '"';
}
} else {
row += ',';
if (signal.valueTable) {
row += ',';
}
}
});
@@ -690,7 +759,9 @@ const char graph_viewer_html[] PROGMEM = R"rawliteral(
chart.data = [];
chart.times = [];
chart.labels = [];
chart.rawValues = [];
chart.currentValue = 0;
chart.currentText = '';
chart.draw();
});