import { ReaderCommand } from '../types'; import { calculateCRC16, getCRCBytes } from '../utils/crc16'; export class RfidProtocol { /** * Constructs a command frame: [Len, Adr, Cmd, Data..., CRC_LSB, CRC_MSB] */ static buildCommand(address: number, cmd: ReaderCommand, data: number[] = []): Uint8Array { const len = data.length + 4; // Len byte itself is not included in length value logic usually, but manual says: "Len equals length of Data[] plus 4" // Frame except CRC const frameHeader = [len, address, cmd, ...data]; // Calculate CRC on [Len, Adr, Cmd, Data...] const crc = calculateCRC16(new Uint8Array(frameHeader)); const [lsb, msb] = getCRCBytes(crc); return new Uint8Array([...frameHeader, lsb, msb]); } /** * Helper to interpret status codes from the reader */ static getStatusDescription(status: number): string { switch (status) { case 0x00: return "Success"; case 0x01: return "Return before Inventory finished"; case 0x02: return "Inventory Timeout"; case 0x03: return "More data follows"; case 0x04: return "Reader memory full"; case 0x05: return "Access Password Error"; case 0x09: return "Kill Tag Error"; case 0x0A: return "Kill Password cannot be zero"; case 0x0B: return "Tag does not support command"; case 0x0C: return "Access Password cannot be zero (Locked)"; case 0x0D: return "Tag is protected, cannot set again"; case 0x0E: return "Tag is unprotected, no need to reset"; case 0x10: return "Locked bytes, write fail"; case 0x11: return "Cannot lock tag"; case 0x12: return "Already locked, cannot lock again"; case 0x13: return "Save Parameter Failed"; case 0x14: return "Cannot adjust power"; case 0x15: return "Return before Inventory finished (6B)"; case 0x16: return "Inventory Timeout (6B)"; case 0x17: return "More data follows (6B)"; case 0x18: return "Reader memory full (6B)"; case 0x19: return "Not Support Command or Access Password Error"; case 0xF9: return "Command Execution Error"; case 0xFA: return "Poor Communication / Tag Inoperable"; case 0xFB: return "No Tag Operable"; case 0xFC: return "Tag Returned Error Code"; // Check data for specific code case 0xFD: return "Command Length Wrong"; case 0xFE: return "Illegal Command / CRC Error"; case 0xFF: return "Parameter Error"; case 0x30: return "Communication Error"; default: return `Unknown Status (0x${status.toString(16).toUpperCase().padStart(2, '0')})`; } } /** * Helper to interpret EPC C1G2 Tag specific error codes (Status 0xFC) */ static getTagErrorDescription(errorCode: number): string { switch (errorCode) { case 0x00: return "Other error"; case 0x03: return "Memory overrun or EPC length not supported"; case 0x04: return "Memory locked"; case 0x0B: return "Insufficient power"; case 0x0F: return "Non-specific error"; default: return `Unknown Tag Error (0x${errorCode.toString(16).toUpperCase().padStart(2, '0')})`; } } }