00003 전송웹추가
웹페이지 하나 더 추가 하고 싶은데 (포트생성) 이 신규 페이지는 can data를 송신 하는 목적의 페이지로 can 신호id, messagy type, message data(hex) 를 설정할 수 있게 하고 여러 신호들을 조합으로 송신할 수 있게, 그리고 특정 시간마다 송신할 수 있게 기능을 추가 할 수 있어? 해당페이지는 차후 수정이 용이하게 파일도 추가했으면 좋겠어
This commit is contained in:
92
index.h
92
index.h
@@ -32,6 +32,21 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
}
|
||||
.header h1 { font-size: 2.5em; margin-bottom: 10px; }
|
||||
.header p { opacity: 0.9; font-size: 1.1em; }
|
||||
.nav {
|
||||
background: #2c3e50;
|
||||
padding: 15px 30px;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
.nav a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.nav a:hover { background: #34495e; }
|
||||
.nav a.active { background: #3498db; }
|
||||
.content { padding: 30px; }
|
||||
.status-grid {
|
||||
display: grid;
|
||||
@@ -155,6 +170,12 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
<h1>🚗 ESP32 CAN Logger</h1>
|
||||
<p>Real-time CAN Bus Monitor & Data Logger</p>
|
||||
</div>
|
||||
|
||||
<div class="nav">
|
||||
<a href="/" class="active">📊 Monitor</a>
|
||||
<a href="/transmit">📡 Transmit</a>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="status-grid">
|
||||
<div class="status-card" id="logging-status">
|
||||
@@ -223,8 +244,8 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
<script>
|
||||
let ws;
|
||||
let reconnectInterval;
|
||||
let canMessages = {}; // CAN ID별 메시지 저장 객체
|
||||
let messageOrder = []; // 메시지 표시 순서
|
||||
let canMessages = {};
|
||||
let messageOrder = [];
|
||||
|
||||
function initWebSocket() {
|
||||
ws = new WebSocket('ws://' + window.location.hostname + ':81');
|
||||
@@ -232,11 +253,7 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
ws.onopen = function() {
|
||||
console.log('WebSocket connected');
|
||||
clearInterval(reconnectInterval);
|
||||
|
||||
// 연결 직후 파일 목록 요청
|
||||
setTimeout(() => {
|
||||
refreshFiles();
|
||||
}, 500);
|
||||
setTimeout(() => { refreshFiles(); }, 500);
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
@@ -253,13 +270,12 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
} else if (data.type === 'can') {
|
||||
addCanMessage(data);
|
||||
} else if (data.type === 'canBatch') {
|
||||
// 일괄 메시지 처리
|
||||
updateCanBatch(data.messages);
|
||||
} else if (data.type === 'files') {
|
||||
console.log('파일 목록 수신:', data.files ? data.files.length : 0);
|
||||
if (data.error) {
|
||||
document.getElementById('file-list').innerHTML =
|
||||
`<p style="text-align: center; color: #e74c3c;">Error: ${data.error}</p>`;
|
||||
'<p style="text-align: center; color: #e74c3c;">Error: ' + data.error + '</p>';
|
||||
} else {
|
||||
updateFileList(data.files);
|
||||
}
|
||||
@@ -292,7 +308,6 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
sdCard.querySelector('.value').textContent = 'NOT READY';
|
||||
}
|
||||
|
||||
// 현재 파일명 표시
|
||||
if (data.currentFile && data.currentFile !== '') {
|
||||
fileCard.classList.add('status-on');
|
||||
fileCard.classList.remove('status-off');
|
||||
@@ -308,80 +323,60 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
|
||||
function addCanMessage(data) {
|
||||
const canId = data.id;
|
||||
|
||||
// 새로운 CAN ID인 경우 순서에 추가
|
||||
if (!canMessages[canId]) {
|
||||
messageOrder.push(canId);
|
||||
}
|
||||
|
||||
// CAN ID별로 최신 메시지 저장
|
||||
canMessages[canId] = {
|
||||
timestamp: data.timestamp,
|
||||
dlc: data.dlc,
|
||||
data: data.data,
|
||||
updateCount: (canMessages[canId]?.updateCount || 0) + 1
|
||||
updateCount: data.count
|
||||
};
|
||||
}
|
||||
|
||||
function updateCanBatch(messages) {
|
||||
// 여러 메시지를 한 번에 처리
|
||||
messages.forEach(msg => {
|
||||
const canId = msg.id;
|
||||
|
||||
if (!canMessages[canId]) {
|
||||
messageOrder.push(canId);
|
||||
}
|
||||
|
||||
// ESP32에서 받은 실제 count 값 사용
|
||||
canMessages[canId] = {
|
||||
timestamp: msg.timestamp,
|
||||
dlc: msg.dlc,
|
||||
data: msg.data,
|
||||
updateCount: msg.count // 실제 수신 횟수
|
||||
updateCount: msg.count
|
||||
};
|
||||
});
|
||||
|
||||
// 한 번만 테이블 업데이트
|
||||
updateCanTable();
|
||||
}
|
||||
|
||||
function updateCanTable() {
|
||||
const tbody = document.getElementById('can-messages');
|
||||
|
||||
// 기존 행들의 ID를 Map으로 저장
|
||||
const existingRows = new Map();
|
||||
Array.from(tbody.rows).forEach(row => {
|
||||
existingRows.set(row.dataset.canId, row);
|
||||
});
|
||||
|
||||
// CAN ID 순서대로 업데이트
|
||||
messageOrder.forEach(canId => {
|
||||
const msg = canMessages[canId];
|
||||
let row = existingRows.get(canId);
|
||||
|
||||
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);
|
||||
} else {
|
||||
// 새 행 추가
|
||||
row = tbody.insertRow();
|
||||
row.dataset.canId = canId;
|
||||
|
||||
row.innerHTML = `
|
||||
<td class="mono">0x${canId}</td>
|
||||
<td>${msg.dlc}</td>
|
||||
<td class="mono">${msg.data}</td>
|
||||
<td>${msg.updateCount}</td>
|
||||
<td>${msg.timestamp}</td>
|
||||
`;
|
||||
|
||||
row.innerHTML =
|
||||
'<td class="mono">0x' + canId + '</td>' +
|
||||
'<td>' + msg.dlc + '</td>' +
|
||||
'<td class="mono">' + msg.data + '</td>' +
|
||||
'<td>' + msg.updateCount + '</td>' +
|
||||
'<td>' + msg.timestamp + '</td>';
|
||||
row.classList.add('flash-row');
|
||||
setTimeout(() => row.classList.remove('flash-row'), 300);
|
||||
}
|
||||
@@ -396,29 +391,26 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
return;
|
||||
}
|
||||
|
||||
// 파일명 기준으로 내림차순 정렬 (최신 파일이 위로)
|
||||
files.sort((a, b) => {
|
||||
// canlog_XXXXX.bin에서 숫자 부분 추출
|
||||
const numA = parseInt(a.name.match(/\d+/)?.[0] || '0');
|
||||
const numB = parseInt(b.name.match(/\d+/)?.[0] || '0');
|
||||
return numB - numA; // 내림차순
|
||||
return numB - numA;
|
||||
});
|
||||
|
||||
fileList.innerHTML = '';
|
||||
files.forEach(file => {
|
||||
const fileItem = document.createElement('div');
|
||||
fileItem.className = 'file-item';
|
||||
fileItem.innerHTML = `
|
||||
<div>
|
||||
<span class="file-name">📄 ${file.name}</span>
|
||||
<span class="file-size">(${formatBytes(file.size)})</span>
|
||||
</div>
|
||||
<button class="download-btn" onclick="downloadFile('${file.name}')">⬇ Download</button>
|
||||
`;
|
||||
fileItem.innerHTML =
|
||||
'<div>' +
|
||||
'<span class="file-name">📄 ' + file.name + '</span>' +
|
||||
'<span class="file-size">(' + formatBytes(file.size) + ')</span>' +
|
||||
'</div>' +
|
||||
'<button class="download-btn" onclick="downloadFile(\'' + file.name + '\')">⬇ Download</button>';
|
||||
fileList.appendChild(fileItem);
|
||||
});
|
||||
|
||||
console.log(`파일 목록 업데이트: ${files.length}개 (최신순)`);
|
||||
console.log('파일 목록 업데이트: ' + files.length + '개 (최신순)');
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
@@ -454,8 +446,6 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
}
|
||||
|
||||
initWebSocket();
|
||||
|
||||
// 페이지 로드 시 파일 목록 요청 (2초 후 재시도)
|
||||
setTimeout(() => {
|
||||
if (!document.getElementById('file-list').querySelector('.file-item')) {
|
||||
console.log('파일 목록 재요청');
|
||||
|
||||
Reference in New Issue
Block a user