diff --git a/ESP32_CAN_Logger.ino b/ESP32_CAN_Logger.ino
index 7877ff2..d1f4d38 100644
--- a/ESP32_CAN_Logger.ino
+++ b/ESP32_CAN_Logger.ino
@@ -17,6 +17,7 @@
#include "index.h"
#include "transmit.h"
#include "graph.h" // 그래프 페이지 추가
+#include "graph_viewer.h" // 새로 추가
// GPIO 핀 정의
#define CAN_INT_PIN 27
@@ -742,7 +743,12 @@ void setup() {
server.on("/graph", HTTP_GET, []() {
server.send_P(200, "text/html", graph_html);
});
-
+
+ // ⭐ 새로 추가: 그래프 뷰어 페이지
+ server.on("/graph-view", HTTP_GET, []() {
+ server.send_P(200, "text/html", graph_viewer_html);
+ });
+
server.on("/download", HTTP_GET, []() {
if (server.hasArg("file")) {
String filename = "/" + server.arg("file");
diff --git a/graph.h b/graph.h
index 694473a..3decd7c 100644
--- a/graph.h
+++ b/graph.h
@@ -206,8 +206,12 @@ const char graph_html[] PROGMEM = R"rawliteral(
- Real-time Graphs
-
+
+
+ ℹ️ Info: Click "Start" to open a new window with real-time graphs.
+ Your selected signals will be saved automatically.
+
+
@@ -518,6 +522,9 @@ const char graph_html[] PROGMEM = R"rawliteral(
}
document.getElementById('signal-section').style.display = 'block';
+
+ // 저장된 선택 복원
+ setTimeout(() => loadSelectedSignals(), 100);
}
function toggleSignal(signal, element) {
@@ -535,6 +542,9 @@ const char graph_html[] PROGMEM = R"rawliteral(
selectedSignals.push(signal);
element.classList.add('selected');
}
+
+ // 선택한 신호 저장
+ saveSelectedSignals();
}
function clearSelection() {
@@ -542,6 +552,69 @@ const char graph_html[] PROGMEM = R"rawliteral(
document.querySelectorAll('.signal-item').forEach(item => {
item.classList.remove('selected');
});
+
+ // 저장된 선택 삭제
+ saveSelectedSignals();
+ }
+
+ // 선택한 신호를 localStorage에 저장
+ function saveSelectedSignals() {
+ try {
+ localStorage.setItem('selected_signals', JSON.stringify(selectedSignals));
+ console.log('Saved', selectedSignals.length, 'signals');
+ } catch(e) {
+ console.error('Failed to save selected signals:', e);
+ }
+ }
+
+ // localStorage에서 선택한 신호 복원
+ function loadSelectedSignals() {
+ try {
+ const saved = localStorage.getItem('selected_signals');
+ if (saved) {
+ const signals = JSON.parse(saved);
+
+ // 신호 목록이 표시된 후에 선택 상태 복원
+ signals.forEach(savedSignal => {
+ // DBC에 해당 신호가 있는지 확인
+ let found = false;
+ for (let msgId in dbcData.messages) {
+ const msg = dbcData.messages[msgId];
+ const signal = msg.signals.find(s =>
+ s.messageId === savedSignal.messageId && s.name === savedSignal.name);
+
+ if (signal) {
+ selectedSignals.push(signal);
+ found = true;
+ break;
+ }
+ }
+ });
+
+ // UI 업데이트
+ document.querySelectorAll('.signal-item').forEach(item => {
+ const signalName = item.querySelector('.signal-name').textContent;
+ const signalInfo = item.querySelector('.signal-info').textContent;
+ const idMatch = signalInfo.match(/ID: 0x([0-9A-F]+)/);
+
+ if (idMatch) {
+ const msgId = parseInt(idMatch[1], 16);
+ const isSelected = selectedSignals.some(s =>
+ s.messageId === msgId && s.name === signalName);
+
+ if (isSelected) {
+ item.classList.add('selected');
+ }
+ }
+ });
+
+ if (selectedSignals.length > 0) {
+ showStatus('Restored ' + selectedSignals.length + ' selected signals', 'success');
+ }
+ }
+ } catch(e) {
+ console.error('Failed to load selected signals:', e);
+ }
}
function startGraphing() {
@@ -550,67 +623,34 @@ const char graph_html[] PROGMEM = R"rawliteral(
return;
}
- graphing = true;
- startTime = Date.now(); // 시작 시간 기록
- createGraphs();
- showStatus('Graphing ' + selectedSignals.length + ' signals', 'success');
+ // 선택한 신호 저장 (새 창에서 사용)
+ saveSelectedSignals();
+
+ // 새 창 열기
+ const width = 1200;
+ const height = 800;
+ const left = (screen.width - width) / 2;
+ const top = (screen.height - height) / 2;
+
+ window.open(
+ '/graph-view',
+ 'CAN_Graph_Viewer',
+ 'width=' + width + ',height=' + height + ',left=' + left + ',top=' + top + ',resizable=yes,scrollbars=yes'
+ );
+
+ showStatus('Graph viewer opened in new window', 'success');
}
function stopGraphing() {
- graphing = false;
- showStatus('Stopped', 'success');
+ showStatus('Use the stop button in the graph viewer window', 'error');
}
function createGraphs() {
- const graphsDiv = document.getElementById('graphs');
- graphsDiv.innerHTML = '';
- charts = {};
-
- selectedSignals.forEach((signal, index) => {
- const container = document.createElement('div');
- container.className = 'graph-container';
-
- const canvas = document.createElement('canvas');
- canvas.id = 'chart-' + index;
-
- container.innerHTML =
- '';
-
- container.appendChild(canvas);
- graphsDiv.appendChild(container);
-
- charts[index] = new SimpleChart(canvas, signal, index);
- });
+ // 이 함수는 새 창에서 사용됨
}
function processCANData(messages) {
- // 경과 시간 계산 (초 단위, 소수점 1자리)
- const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(1);
-
- messages.forEach(canMsg => {
- const idStr = canMsg.id.replace(/\s/g, '').toUpperCase();
- const msgId = parseInt(idStr, 16);
-
- selectedSignals.forEach((signal, index) => {
- if (signal.messageId === msgId && charts[index]) {
- 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 : '');
- }
- } catch(e) {
- console.error('Error decoding signal', signal.name, ':', e);
- }
- }
- });
- });
+ // 이 함수는 새 창에서 사용됨
}
function decodeSignal(signal, hexData) {
diff --git a/graph_viewer.h b/graph_viewer.h
new file mode 100644
index 0000000..879237f
--- /dev/null
+++ b/graph_viewer.h
@@ -0,0 +1,468 @@
+#ifndef GRAPH_VIEWER_H
+#define GRAPH_VIEWER_H
+
+const char graph_viewer_html[] PROGMEM = R"rawliteral(
+
+
+
+
+
+ CAN Signal Graph Viewer
+
+
+
+
+
+
+
+
+
+
+
+ Connecting...
+
+
+
+
+
+
+)rawliteral";
+
+#endif
\ No newline at end of file
diff --git a/index.h b/index.h
index ddffeeb..8b2d699 100644
--- a/index.h
+++ b/index.h
@@ -274,6 +274,23 @@ const char index_html[] PROGMEM = R"rawliteral(
let canMessages = {};
let messageOrder = [];
+ // CAN 속도 설정 저장 및 복원
+ function saveCanSpeed() {
+ const speed = document.getElementById('can-speed').value;
+ try {
+ localStorage.setItem('canSpeed', speed);
+ } catch(e) {}
+ }
+
+ function loadCanSpeed() {
+ try {
+ const savedSpeed = localStorage.getItem('canSpeed');
+ if (savedSpeed !== null) {
+ document.getElementById('can-speed').value = savedSpeed;
+ }
+ } catch(e) {}
+ }
+
function initWebSocket() {
ws = new WebSocket('ws://' + window.location.hostname + ':81');