can dbc graph Value Table 파싱
This commit is contained in:
57
graph.h
57
graph.h
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user