import React, { useState, useEffect } from 'react'; import { MemoryBank, TagData } from '../types'; import { Lock, HardDriveDownload, HardDriveUpload, Search, RefreshCw, ChevronDown, Tag } from 'lucide-react'; import { hexStringToBytes, hexToAscii, asciiToHex } from '../utils/crc16'; interface Props { tags: TagData[]; onScan: () => void; isScanning: boolean; onRead: (bank: MemoryBank, ptr: number, len: number, password: string, targetEpc: string) => Promise; onWrite: (bank: MemoryBank, ptr: number, data: string, password: string, targetEpc: string) => Promise; onWriteEpc: (newEpc: string, password: string) => Promise; selectedEpc: string; // From parent (legacy), we will use local state mainly readResult: string | null; } export const MemoryPanel: React.FC = ({ tags, onScan, isScanning, onRead, onWrite, onWriteEpc, selectedEpc: initialEpc, readResult }) => { const [targetEpc, setTargetEpc] = useState(initialEpc); const [bank, setBank] = useState(MemoryBank.EPC); const [ptr, setPtr] = useState(2); // Default to 2 for EPC bank const [len, setLen] = useState(4); const [password, setPassword] = useState("00000000"); const [writeData, setWriteData] = useState(""); // Unified Data Mode: Controls both Read display and Write input interpretation const [dataMode, setDataMode] = useState<'hex' | 'ascii'>('hex'); // Update local state if parent prop changes (optional sync) useEffect(() => { if (initialEpc) setTargetEpc(initialEpc); }, [initialEpc]); // If tags list updates and we don't have a target, auto-select the first one useEffect(() => { if (!targetEpc && tags.length > 0) { setTargetEpc(tags[0].epc); } }, [tags, targetEpc]); // Auto-update Pointer default based on Bank to prevent Parameter Errors useEffect(() => { if (bank === MemoryBank.EPC) { setPtr(2); // Start at 2 to skip CRC (0) and PC (1) which are often protected } else { setPtr(0); } }, [bank]); const handleRead = () => { if (!targetEpc) { alert("Please select or scan a tag first."); return; } onRead(bank, ptr, len, password, targetEpc); }; const toggleDataMode = (newMode: 'hex' | 'ascii') => { if (newMode === dataMode) return; // Convert current Write Input Data to match new mode // This prevents the user from having to manually convert what they just typed if (newMode === 'ascii') { // Switching Hex -> Ascii setWriteData(hexToAscii(writeData)); } else { // Switching Ascii -> Hex setWriteData(asciiToHex(writeData)); } setDataMode(newMode); }; const getHexForWrite = () => { if (dataMode === 'ascii') { return asciiToHex(writeData); } return writeData; }; const handleWrite = () => { if (!targetEpc) { alert("Please select or scan a tag first."); return; } const hexPayload = getHexForWrite(); onWrite(bank, ptr, hexPayload, password, targetEpc); }; const handleInitializeEpc = () => { const hexPayload = getHexForWrite(); // Validate locally but allow error logging if (!hexStringToBytes(hexPayload)) { // Pass invalid data to parent to trigger the "Invalid EPC Data" log onWriteEpc(hexPayload, password); return; } // Confirm with user only if data looks valid if (confirm("This will overwrite the EPC of a single tag in the field using Command 0x04. Proceed?")) { onWriteEpc(hexPayload, password); } }; return (

Memory Operations

{/* Unified Mode Toggle */}
{/* Configuration Column */}
{/* Tag Selection Area */}
setTargetEpc(e.target.value)} placeholder="Scan or type EPC..." className="w-full bg-white border border-slate-300 text-slate-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 font-mono" /> {tags.length > 0 && (
)} {/* Invisible Select overlay for dropdown behavior */} {tags.length > 0 && ( )}

{tags.length > 0 ? `${tags.length} tags found. Select one to target.` : "Click Scan to find nearby tags."}

setPassword(e.target.value)} maxLength={8} className="w-full bg-white border border-slate-300 text-slate-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 pl-9 font-mono" />
setPtr(Number(e.target.value))} className="w-full bg-white border border-slate-300 text-sm rounded-lg p-2.5" />
setLen(Number(e.target.value))} className="w-full bg-white border border-slate-300 text-sm rounded-lg p-2.5" />
{bank === MemoryBank.EPC && (
Note: Start Address 0 is CRC, 1 is PC. Writing to these may fail (Error 0xFF). EPC data typically starts at address 2.
)}
{/* Action Column */}
{/* Read Section */}
{readResult ? (dataMode === 'hex' ? readResult : hexToAscii(readResult)) : "No data read..." }
{/* Write Section */}
setWriteData(e.target.value)} placeholder={dataMode === 'hex' ? "e.g., A1 B2 C3..." : "Type text here..."} className="w-full bg-white border border-slate-300 text-sm rounded p-2 mb-2 font-mono" />
{bank === MemoryBank.EPC && ( )}
); };