한글화

This commit is contained in:
backuppc
2026-01-23 14:37:31 +09:00
parent 314a6cc89c
commit fb03c189b2
2 changed files with 111 additions and 53 deletions

82
App.tsx
View File

@@ -8,7 +8,7 @@ import { QuickTestPanel } from './components/QuickTestPanel';
import { ConnectionStatus, TagData, ReaderCommand, FrequencyBand, MemoryBank, LogEntry, SerialPort, ReaderInfo, QuickTestConfig } from './types';
import { RfidProtocol } from './services/rfidService';
import { calculateCRC16, bytesToHexString, hexStringToBytes, hexToAscii, asciiToHex } from './utils/crc16';
import { Terminal, Settings, LayoutDashboard, Database, Zap, ChevronUp, ChevronDown, Wifi, WifiOff } from 'lucide-react';
import { Terminal, Settings, LayoutDashboard, Database, Zap, ChevronUp, ChevronDown, Wifi, WifiOff, AlertCircle, AlertTriangle } from 'lucide-react';
const App: React.FC = () => {
// Serial State
@@ -18,7 +18,7 @@ const App: React.FC = () => {
const [address, setAddress] = useState(0xFF); // Broadcast address default
// App State - Set Quick Test as Default
const [activeTab, setActiveTab] = useState<'connect' | 'inventory' | 'settings' | 'memory' | 'quicktest'>('quicktest');
const [activeTab, setActiveTab] = useState<'connect' | 'inventory' | 'settings' | 'memory' | 'quicktest'>('connect');
const [logs, setLogs] = useState<LogEntry[]>([]);
const [tags, setTags] = useState<TagData[]>([]);
const [isScanning, setIsScanning] = useState(false);
@@ -28,6 +28,21 @@ const App: React.FC = () => {
// Log Visibility State
const [isLogExpanded, setIsLogExpanded] = useState(false);
// Global Error Message State
const [globalMessage, setGlobalMessage] = useState<{ text: string, type: 'error' | 'info' } | null>(null);
const messageTimerRef = useRef<number | null>(null);
const showTemporaryMessage = (text: string, type: 'error' | 'info' = 'error') => {
if (messageTimerRef.current) {
window.clearTimeout(messageTimerRef.current);
}
setGlobalMessage({ text, type });
messageTimerRef.current = window.setTimeout(() => {
setGlobalMessage(null);
messageTimerRef.current = null;
}, 5000);
};
// Quick Action Config (Persisted in localStorage)
const [quickTestConfig, setQuickTestConfig] = useState<QuickTestConfig>(() => {
const saved = localStorage.getItem('quickTestConfig');
@@ -357,20 +372,26 @@ const App: React.FC = () => {
if (status === 0x01 || status === 0x02 || status === 0x03 || status === 0x04) {
handleInventoryResponse(data);
} else if (status === 0xFB) {
addLog('INFO', RfidProtocol.getStatusDescription(status));
const msg = RfidProtocol.getStatusDescription(status);
addLog('INFO', msg);
showTemporaryMessage(msg, 'info'); // Show as Info
} else {
let errorDetail = RfidProtocol.getStatusDescription(status);
addLog('WARN', `Inventory Failed: ${errorDetail}`, `Status: 0x${status.toString(16).toUpperCase()}`);
showTemporaryMessage(`Inventory Failed: ${errorDetail}`, 'error');
}
} else {
if (status === 0xFB) {
addLog('INFO', RfidProtocol.getStatusDescription(status));
const msg = RfidProtocol.getStatusDescription(status);
addLog('INFO', msg);
showTemporaryMessage(msg, 'info');
} else {
let errorDetail = RfidProtocol.getStatusDescription(status);
if (status === 0xFC && data.length > 0) {
errorDetail += ` - ${RfidProtocol.getTagErrorDescription(data[0])}`;
}
addLog('ERROR', `Command 0x${reCmd.toString(16).toUpperCase()} Failed`, errorDetail);
showTemporaryMessage(`Command Failed: ${errorDetail}`, 'error');
if (reCmd === ReaderCommand.WRITE_DATA_G2 && performingQuickWriteRef.current) {
performingQuickWriteRef.current = false;
@@ -455,6 +476,10 @@ const App: React.FC = () => {
// --- Operations ---
const toggleScanning = useCallback(() => {
if (status !== ConnectionStatus.CONNECTED) {
alert("리더기가 연결되지 않았습니다! 먼저 시리얼 포트를 통해 연결해주세요.");
return;
}
if (isScanning) {
if (scanIntervalRef.current) clearInterval(scanIntervalRef.current);
scanIntervalRef.current = null;
@@ -465,13 +490,11 @@ const App: React.FC = () => {
runScan();
scanIntervalRef.current = window.setInterval(runScan, 500);
}
}, [isScanning, address]);
}, [isScanning, address, status]);
const handleQuickRead = () => {
if (status !== ConnectionStatus.CONNECTED) {
if (!performingQuickWriteRef.current) {
alert("Reader is not connected! Please connect via serial port first.");
}
alert("리더기가 연결되지 않았습니다! 먼저 시리얼 포트를 통해 연결해주세요.");
return;
}
@@ -485,7 +508,7 @@ const App: React.FC = () => {
const handleQuickWrite = (inputValue: string) => {
if (status !== ConnectionStatus.CONNECTED) {
alert("Reader is not connected! Please connect via serial port first.");
alert("리더기가 연결되지 않았습니다! 먼저 시리얼 포트를 통해 연결해주세요.");
return;
}
@@ -515,12 +538,16 @@ const App: React.FC = () => {
setPendingQuickAction(null);
performingQuickWriteRef.current = false;
verificationRetriesRef.current = 0;
addLog('INFO', 'Quick Test Cancelled by user');
addLog('INFO', '사용자에 의해 퀵 테스트 취소됨');
};
const clearTags = () => setTags([]);
const handleGetInfo = async () => {
if (status !== ConnectionStatus.CONNECTED) {
alert("리더기가 연결되지 않았습니다! 먼저 시리얼 포트를 통해 연결해주세요.");
return;
}
addLog('INFO', 'Requesting Reader Info...');
await sendCommand(ReaderCommand.GET_INFO);
};
@@ -533,6 +560,10 @@ const App: React.FC = () => {
maxFreq: number;
band: FrequencyBand;
}) => {
if (status !== ConnectionStatus.CONNECTED) {
alert("리더기가 연결되지 않았습니다! 먼저 시리얼 포트를 통해 연결해주세요.");
return;
}
await sendCommand(ReaderCommand.SET_POWER, [settings.power]);
if (settings.address !== address) {
await sendCommand(ReaderCommand.SET_ADDRESS, [settings.address]);
@@ -542,6 +573,10 @@ const App: React.FC = () => {
};
const handleFactoryReset = async () => {
if (status !== ConnectionStatus.CONNECTED) {
alert("리더기가 연결되지 않았습니다! 먼저 시리얼 포트를 통해 연결해주세요.");
return;
}
addLog('INFO', 'Sending Factory Reset...');
};
@@ -555,6 +590,10 @@ const App: React.FC = () => {
};
const handleFetchTids = async () => {
if (status !== ConnectionStatus.CONNECTED) {
alert("리더기가 연결되지 않았습니다! 먼저 시리얼 포트를 통해 연결해주세요.");
return;
}
if (isScanning) {
if (scanIntervalRef.current) clearInterval(scanIntervalRef.current);
scanIntervalRef.current = null;
@@ -580,6 +619,10 @@ const App: React.FC = () => {
};
const handleMemoryRead = async (bank: MemoryBank, ptr: number, len: number, pwd: string, targetEpc: string) => {
if (status !== ConnectionStatus.CONNECTED) {
alert("Reader is not connected! Please connect via serial port first.");
return;
}
const { bytes: epcBytes, words: epcWords } = getEpcData(targetEpc);
let pwdBytes = hexStringToBytes(pwd) || new Uint8Array([0, 0, 0, 0]);
@@ -592,6 +635,10 @@ const App: React.FC = () => {
};
const handleMemoryWrite = async (bank: MemoryBank, ptr: number, dataStr: string, pwd: string, targetEpc: string) => {
if (status !== ConnectionStatus.CONNECTED) {
alert("Reader is not connected! Please connect via serial port first.");
return;
}
const { bytes: epcBytes, words: epcWords } = getEpcData(targetEpc);
let pwdBytes = hexStringToBytes(pwd) || new Uint8Array([0, 0, 0, 0]);
const dataBytes = hexStringToBytes(dataStr);
@@ -637,7 +684,7 @@ const App: React.FC = () => {
<Database className="text-blue-600" />
UHF Reader
</h1>
<p className="text-xs text-slate-400 mt-1">Web Serial Controller</p>
<p className="text-xs text-slate-400 mt-1">RFID Read & Write Tester</p>
</div>
<div className="p-4 space-y-2 flex-1">
@@ -732,7 +779,18 @@ const App: React.FC = () => {
</button>
</div>
</div>
<main className="flex-1 p-6 overflow-auto">
<main className="flex-1 p-6 overflow-auto relative">
{/* Global Status/Error Banner */}
{globalMessage && (
<div className={`mb-4 px-4 py-3 rounded-lg shadow-sm border flex items-center gap-3 animate-in fade-in slide-in-from-top-2 duration-300 ${globalMessage.type === 'error'
? 'bg-red-50 border-red-200 text-red-700'
: 'bg-amber-50 border-amber-200 text-amber-800'
}`}>
{globalMessage.type === 'error' ? <AlertCircle className="w-5 h-5 shrink-0" /> : <AlertTriangle className="w-5 h-5 shrink-0" />}
<span className="font-medium text-sm">{globalMessage.text}</span>
</div>
)}
{activeTab === 'connect' && (
<div className="space-y-4 max-w-2xl mx-auto">
<h2 className="text-lg font-bold text-slate-800">Connection Manager</h2>