transmit 창 메세지 리스트 저장불러오기 기능 추가, monitor 창 canspeed 유지기능 추가

This commit is contained in:
2025-10-06 17:16:28 +00:00
parent 1bf6186305
commit d629b076db
2 changed files with 247 additions and 18 deletions

View File

@@ -119,6 +119,10 @@ const char transmit_html[] PROGMEM = R"rawliteral(
background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);
color: white;
}
.btn-warning {
background: linear-gradient(135deg, #f2994a 0%, #f2c94c 100%);
color: white;
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0,0,0,0.3); }
.message-list {
@@ -162,10 +166,6 @@ const char transmit_html[] PROGMEM = R"rawliteral(
font-size: 0.85em;
word-break: break-all;
}
.message-interval {
color: #888;
font-size: 0.8em;
}
.message-controls {
display: flex;
gap: 5px;
@@ -197,6 +197,63 @@ const char transmit_html[] PROGMEM = R"rawliteral(
}
.status-bar span { font-weight: 600; }
.preset-manager {
background: #fff3cd;
padding: 15px;
border-radius: 10px;
margin-bottom: 15px;
border-left: 4px solid #f2994a;
}
.preset-controls {
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 10px;
}
.preset-controls input {
flex: 1;
min-width: 200px;
padding: 8px;
border: 2px solid #f2994a;
border-radius: 5px;
font-size: 0.9em;
}
.preset-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 10px;
margin-top: 10px;
}
.preset-item {
background: white;
padding: 10px;
border-radius: 5px;
border: 2px solid #f2c94c;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.3s;
}
.preset-item:hover {
transform: translateX(5px);
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.preset-name {
font-weight: 600;
color: #333;
font-size: 0.9em;
}
.preset-info {
font-size: 0.75em;
color: #666;
margin-top: 3px;
}
.preset-buttons {
display: flex;
gap: 5px;
}
@media (max-width: 768px) {
body { padding: 5px; }
.header h1 { font-size: 1.5em; }
@@ -218,17 +275,6 @@ const char transmit_html[] PROGMEM = R"rawliteral(
.message-controls {
justify-content: flex-start;
}
.control-row {
flex-direction: column;
align-items: stretch;
}
.control-row > * {
width: 100%;
}
.nav {
padding: 8px;
gap: 5px;
}
.nav a {
padding: 8px 10px;
font-size: 0.85em;
@@ -241,6 +287,9 @@ const char transmit_html[] PROGMEM = R"rawliteral(
gap: 8px;
text-align: center;
}
.preset-list {
grid-template-columns: 1fr;
}
}
</style>
</head>
@@ -263,6 +312,17 @@ const char transmit_html[] PROGMEM = R"rawliteral(
<span id="tx-count">Sent: 0</span>
</div>
<h2>Message List Presets</h2>
<div class="preset-manager">
<div class="preset-controls">
<input type="text" id="preset-name" placeholder="Enter preset name...">
<button class="btn btn-warning" onclick="savePreset()">Save Current List</button>
</div>
<div class="preset-list" id="preset-list">
<p style="text-align: center; color: #666; font-size: 0.9em;">No saved presets</p>
</div>
</div>
<h2>Add CAN Message</h2>
<div class="message-form">
<div class="form-row">
@@ -358,6 +418,127 @@ const char transmit_html[] PROGMEM = R"rawliteral(
};
}
// 프리셋 관리
function savePreset() {
const presetName = document.getElementById('preset-name').value.trim();
if (!presetName) {
alert('Please enter a preset name!');
return;
}
if (messages.length === 0) {
alert('No messages to save!');
return;
}
try {
let presets = JSON.parse(localStorage.getItem('tx_presets') || '{}');
if (presets[presetName] && !confirm('Preset "' + presetName + '" already exists. Overwrite?')) {
return;
}
presets[presetName] = {
messages: JSON.parse(JSON.stringify(messages)),
savedAt: new Date().toISOString(),
count: messages.length
};
localStorage.setItem('tx_presets', JSON.stringify(presets));
document.getElementById('preset-name').value = '';
loadPresetList();
alert('Preset "' + presetName + '" saved successfully!');
} catch(e) {
console.error('Failed to save preset:', e);
alert('Failed to save preset!');
}
}
function loadPreset(presetName) {
try {
const presets = JSON.parse(localStorage.getItem('tx_presets') || '{}');
const preset = presets[presetName];
if (!preset) {
alert('Preset not found!');
return;
}
if (messages.length > 0 && !confirm('Current message list will be replaced. Continue?')) {
return;
}
stopAll();
messages = JSON.parse(JSON.stringify(preset.messages));
messages.forEach(msg => msg.active = false);
updateMessageList();
alert('Loaded preset "' + presetName + '" with ' + preset.count + ' messages');
} catch(e) {
console.error('Failed to load preset:', e);
alert('Failed to load preset!');
}
}
function deletePreset(presetName) {
if (!confirm('Delete preset "' + presetName + '"?')) {
return;
}
try {
let presets = JSON.parse(localStorage.getItem('tx_presets') || '{}');
delete presets[presetName];
localStorage.setItem('tx_presets', JSON.stringify(presets));
loadPresetList();
} catch(e) {
console.error('Failed to delete preset:', e);
alert('Failed to delete preset!');
}
}
function loadPresetList() {
const presetListDiv = document.getElementById('preset-list');
try {
const presets = JSON.parse(localStorage.getItem('tx_presets') || '{}');
const presetNames = Object.keys(presets);
if (presetNames.length === 0) {
presetListDiv.innerHTML = '<p style="text-align: center; color: #666; font-size: 0.9em;">No saved presets</p>';
return;
}
presetListDiv.innerHTML = '';
presetNames.sort().forEach(name => {
const preset = presets[name];
const item = document.createElement('div');
item.className = 'preset-item';
const savedDate = new Date(preset.savedAt);
const dateStr = savedDate.toLocaleDateString() + ' ' + savedDate.toLocaleTimeString();
item.innerHTML =
'<div>' +
'<div class="preset-name">' + name + '</div>' +
'<div class="preset-info">' + preset.count + ' messages | ' + dateStr + '</div>' +
'</div>' +
'<div class="preset-buttons">' +
'<button class="btn btn-success btn-small" onclick="loadPreset(\'' + name + '\')">Load</button>' +
'<button class="btn btn-danger btn-small" onclick="deletePreset(\'' + name + '\')">Delete</button>' +
'</div>';
presetListDiv.appendChild(item);
});
} catch(e) {
console.error('Failed to load preset list:', e);
presetListDiv.innerHTML = '<p style="text-align: center; color: #e74c3c; font-size: 0.9em;">Error loading presets</p>';
}
}
function addMessage() {
const id = document.getElementById('can-id').value.toUpperCase();
const type = document.getElementById('msg-type').value;
@@ -501,6 +682,11 @@ const char transmit_html[] PROGMEM = R"rawliteral(
this.value = this.value.toUpperCase().replace(/[^0-9A-F]/g, '');
});
// 페이지 로드 시 프리셋 목록 불러오기
window.addEventListener('load', function() {
loadPresetList();
});
initWebSocket();
</script>
</body>