package com.rfid.uhf288; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.List; /** * RFID设备操作模板 - 模块化设计 * 参考demo项目,但只包含基本读取功能(类似参考项目zy-wcs-czbrwcs中的功能) * Created by chen.lin on 2026/01/10 * * 功能模块: * 1. 连接模块 - 串口连接 / 网络连接(独立) * 2. 读取模块 - 盘点标签(独立) * * 使用方式: * 1. 创建实例 * 2. 选择连接方式(串口或网络) * 3. 连接成功后,调用读取功能 * 4. 使用完毕后关闭连接 */ @Slf4j public class RFIDTemplate { // ==================== 基础组件 ==================== private Device reader; private int portHandle = -1; private byte[] comAddr = new byte[1]; // 连接状态 private boolean isConnected = false; private String connectionType = ""; // "COM" 或 "NET" // ==================== 初始化 ==================== /** * 构造函数 - 加载DLL库 */ public RFIDTemplate() { try { reader = new Device(); comAddr[0] = (byte)255; // 默认地址 log.info("RFID模板初始化成功"); } catch (Exception e) { log.error("RFID模板初始化失败", e); } } // ==================== 模块1: 连接模块 ==================== /** * 【串口连接】打开串口连接 * * @param port 串口号 (1=COM1, 2=COM2, 3=COM3...) * @param baud 波特率 (0=9600, 1=19200, 2=38400, 3=57600, 4=115200, 5=230400, 6=460800, 7=921600) * @return true=成功, false=失败 */ public boolean connectComPort(int port, byte baud) { if (isConnected) { log.warn("已存在连接,请先关闭当前连接"); return false; } try { int[] portHandleArray = new int[1]; int result = reader.OpenComPort(port, comAddr, baud, portHandleArray); if (result == 0) { portHandle = portHandleArray[0]; isConnected = true; connectionType = "COM"; log.info("========== 串口连接成功 =========="); log.info("串口号: COM{}", port); log.info("端口句柄: {}", portHandle); try { int checkAntResult = reader.SetCheckAnt(comAddr, (byte)0x01, portHandle); if (checkAntResult == 0) { // log.info("天线检测开启成功"); } int antResult = reader.SetAntennaMultiplexing(comAddr, (byte)0x0F, portHandle); if (antResult == 0) { log.info("天线多路复用配置成功 - 启用天线1,2,3,4 (0x0F)"); } else { int antennaResult = reader.SetAntenna(comAddr, (byte)0, (byte)0x0F, (byte)0, portHandle); if (antennaResult == 0) { log.info("天线配置成功 - 启用天线1,2,3,4 (0x0F)"); } } } catch (Exception e) { log.warn("配置天线异常: {}", e.getMessage()); } return true; } else { log.error("串口连接失败,错误码: 0x{} ({})", String.format("%02X", result & 0xFF), getErrorCodeMessage(result)); return false; } } catch (UnsatisfiedLinkError e) { log.error("RFID设备DLL方法链接失败 - OpenComPort", e); log.error("错误信息: {}", e.getMessage()); log.error("可能原因:1. DLL版本不匹配 2. 方法签名不匹配 3. DLL未正确加载"); throw e; // 重新抛出异常,让调用者处理 } } /** * 【串口连接】自动尝试多个波特率连接(推荐) * * @param port 串口号 (1=COM1, 2=COM2, 3=COM3...) * @param preferredBaud 首选波特率 (0-7),-1表示使用默认顺序 * @return true=成功, false=失败 */ public boolean connectComPortAuto(int port, int preferredBaud) { if (isConnected) { log.warn("已存在连接,请先关闭当前连接"); return false; } // 波特率映射 byte[] baudRates = {(byte)0, (byte)1, (byte)2, (byte)3, (byte)4, (byte)5, (byte)6, (byte)7}; String[] baudNames = {"9600", "19200", "38400", "57600", "115200", "230400", "460800", "921600"}; // 默认尝试顺序:57600优先 byte[] tryBauds = {(byte)3, (byte)4, (byte)2, (byte)1, (byte)0, (byte)5, (byte)6, (byte)7}; // 如果指定了首选波特率,将其放在最前面 if (preferredBaud >= 0 && preferredBaud <= 7) { byte[] newTryBauds = new byte[tryBauds.length]; newTryBauds[0] = (byte)preferredBaud; int idx = 1; for (byte b : tryBauds) { if (b != preferredBaud) { newTryBauds[idx++] = b; } } tryBauds = newTryBauds; } log.info("========== 串口连接 =========="); log.info("串口号: COM{}", port); log.info("正在尝试连接..."); for (int i = 0; i < tryBauds.length; i++) { byte baud = tryBauds[i]; String baudName = ""; for (int j = 0; j < baudRates.length; j++) { if (baudRates[j] == baud) { baudName = baudNames[j]; break; } } log.info("尝试波特率: {}bps ... ", baudName); int[] portHandleArray = new int[1]; int result = reader.OpenComPort(port, comAddr, baud, portHandleArray); if (result == 0) { portHandle = portHandleArray[0]; isConnected = true; connectionType = "COM"; log.info("✓ 成功!"); log.info("连接成功!波特率: {}bps", baudName); log.info("端口句柄: {}", portHandle); return true; } else { log.warn("✗ 失败"); } try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } log.error("所有波特率尝试均失败!"); return false; } /** * 【网络连接】打开网络连接 * * @param ipAddr IP地址 (如: 192.168.1.190) * @param port 端口号 (默认6000) * @return true=成功, false=失败 */ public boolean connectNetwork(String ipAddr, int port) { if (isConnected) { log.warn("已存在连接,请先关闭当前连接"); return false; } try { int[] portHandleArray = new int[1]; int result = reader.OpenNetPort(port, ipAddr, comAddr, portHandleArray); if (result == 0) { portHandle = portHandleArray[0]; isConnected = true; connectionType = "NET"; log.info("========== 网络连接成功 =========="); log.info("IP地址: {}", ipAddr); log.info("端口号: {}", port); log.info("端口句柄: {}", portHandle); try { int checkAntResult = reader.SetCheckAnt(comAddr, (byte)0x01, portHandle); if (checkAntResult == 0) { // log.info("天线检测开启成功"); } int antResult = reader.SetAntennaMultiplexing(comAddr, (byte)0x0F, portHandle); if (antResult == 0) { log.info("天线多路复用配置成功 - 启用天线1,2,3,4 (0x0F)"); } else { int antennaResult = reader.SetAntenna(comAddr, (byte)0, (byte)0x0F, (byte)0, portHandle); if (antennaResult == 0) { log.info("天线配置成功 - 启用天线1,2,3,4 (0x0F)"); } } } catch (Exception e) { log.warn("配置天线异常: {}", e.getMessage()); } return true; } else { log.error("网络连接失败,错误码: 0x{} ({})", String.format("%02X", result & 0xFF), getErrorCodeMessage(result)); log.error("请检查IP地址和端口是否正确"); return false; } } catch (UnsatisfiedLinkError e) { log.error("RFID设备DLL方法链接失败 - OpenNetPort", e); log.error("错误信息: {}", e.getMessage()); log.error("可能原因:1. DLL版本不匹配 2. 方法签名不匹配 3. DLL未正确加载"); throw e; // 重新抛出异常,让调用者处理 } } /** * 【网络连接】使用默认端口6000连接 * * @param ipAddr IP地址 (如: 192.168.1.190) * @return true=成功, false=失败 */ public boolean connectNetwork(String ipAddr) { return connectNetwork(ipAddr, 6000); } /** * 【连接管理】关闭连接 */ public void disconnect() { if (!isConnected) { log.warn("当前没有连接"); return; } if ("COM".equals(connectionType)) { int result = reader.CloseSpecComPort(portHandle); if (result == 0) { log.info("串口连接已关闭"); } } else if ("NET".equals(connectionType)) { int result = reader.CloseNetPort(portHandle); if (result == 0) { log.info("网络连接已关闭"); } } portHandle = -1; isConnected = false; connectionType = ""; } /** * 【连接管理】检查连接状态 * * @return true=已连接, false=未连接 */ public boolean isConnected() { return isConnected && portHandle >= 0; } /** * 【天线配置】重新配置天线(动态切换天线时使用) * 当物理天线插拔后,调用此方法重新配置天线,确保所有插上的天线都能被检测到 * * @return true=成功, false=失败 */ public boolean reconfigureAntenna() { if (!isConnected()) { log.warn("设备未连接,无法配置天线"); return false; } boolean configured = false; try { int checkAntResult = reader.SetCheckAnt(comAddr, (byte)0x01, portHandle); if (checkAntResult == 0) { // log.info("天线检测开启成功"); configured = true; } int antResult = reader.SetAntennaMultiplexing(comAddr, (byte)0x0F, portHandle); if (antResult == 0) { log.info("天线多路复用配置成功 - 启用天线1,2,3,4 (0x0F)"); configured = true; } else { int antennaResult = reader.SetAntenna(comAddr, (byte)0, (byte)0x0F, (byte)0, portHandle); if (antennaResult == 0) { log.info("天线配置成功 - 启用天线1,2,3,4 (0x0F)"); configured = true; } } return configured; } catch (Exception e) { log.error("重新配置天线异常: {}", e.getMessage(), e); return false; } } /** * 【连接管理】获取连接类型 * * @return "COM"=串口, "NET"=网络, ""=未连接 */ public String getConnectionType() { return connectionType; } // ==================== 模块2: 读取模块 ==================== public List readTags(int qValue, int session, int scanTime) { List tagList = new ArrayList<>(); if (!isConnected()) { return tagList; } byte maskMem = 2; byte[] maskAdr = new byte[2]; byte maskLen = 0; byte[] maskData = new byte[256]; byte maskFlag = 0; byte adrTID = 0; byte lenTID = 6; byte tidFlag = 0; byte target = 0; byte fastFlag = 0; byte[] pEPCList = new byte[20000]; byte[] ant = new byte[1]; int[] totallen = new int[1]; int[] cardNum = new int[1]; byte[] antennaValues = {(byte)0x80, (byte)0x81, (byte)0x82, (byte)0x83}; int[] antennaNumbers = {1, 2, 3, 4}; for (int antIdx = 0; antIdx < antennaValues.length; antIdx++) { byte inAnt = antennaValues[antIdx]; int expectedAntenna = antennaNumbers[antIdx]; java.util.Arrays.fill(pEPCList, (byte)0); totallen[0] = 0; cardNum[0] = 0; int result = reader.Inventory_G2(comAddr, (byte)qValue, (byte)session, maskMem, maskAdr, maskLen, maskData, maskFlag, adrTID, lenTID, tidFlag, target, inAnt, (byte)scanTime, fastFlag, pEPCList, ant, totallen, cardNum, portHandle); if (cardNum[0] > 0 && totallen[0] > 0) { int m = 0; for (int index = 0; index < cardNum[0]; index++) { if (m >= totallen[0] || m >= pEPCList.length) break; int epcLen = pEPCList[m++] & 0xFF; if (epcLen <= 0 || epcLen > 32) break; if (m + epcLen > totallen[0] || m + epcLen > pEPCList.length) break; byte[] epcBytes = new byte[epcLen]; for (int n = 0; n < epcLen; n++) { epcBytes[n] = pEPCList[m++]; } if (m >= totallen[0] || m >= pEPCList.length) break; int rssi = pEPCList[m++] & 0xFF; if (rssi > 127) rssi = rssi - 256; int antennaValue = 0; if (m < totallen[0] && m < pEPCList.length) { antennaValue = pEPCList[m++] & 0xFF; } else { antennaValue = ant[0] & 0xFF; } TagInfo tag = new TagInfo(); tag.epc = bytesToHex(epcBytes); tag.rssi = rssi; if (antennaValue >= 0x80 && antennaValue <= 0x83) { tag.antenna = antennaValue - 0x80 + 1; } else if (antennaValue >= 1 && antennaValue <= 4) { tag.antenna = antennaValue; } else { tag.antenna = expectedAntenna; } tag.timestamp = System.currentTimeMillis(); tagList.add(tag); } } else { if (result != 0) { int errorCode = result & 0xFF; if (errorCode == 0xFB) { continue; } if (errorCode == 0x01) { continue; } if (errorCode == 0xF8) { // log.warn("天线{}盘点失败,错误码: 0x{} ({})", expectedAntenna, String.format("%02X", errorCode), getErrorCodeMessage(result)); } else { // log.debug("天线{}盘点失败,错误码: 0x{} ({})", expectedAntenna, String.format("%02X", errorCode), getErrorCodeMessage(result)); } } } } return tagList; } public List readTags() { return readTags(4, 0, 10); } // ==================== 工具方法 ==================== private String bytesToHex(byte[] bytes) { if (bytes == null) return ""; StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02X", b & 0xFF)); } return sb.toString(); } private String getErrorCodeMessage(int errorCode) { switch (errorCode & 0xFF) { case 0x00: return "无错误,API调用成功"; case 0x01: return "询查时间结束前返回"; case 0x02: return "指定的询查时间溢出"; case 0x03: return "存储器超限或不被支持的PC值"; case 0x04: return "存储器锁定"; case 0x05: return "访问密码错误"; case 0x09: return "销毁密码错误"; case 0x0A: return "销毁密码不能为全0"; case 0x0B: return "电子标签不支持该命令或电源不足"; case 0x0C: return "对该命令,访问密码不能为0"; case 0x0D: return "电子标签已经被设置了读保护,不能再次设置"; case 0x0E: return "电子标签没有被设置读保护,不需要解锁"; case 0x0F: return "非特定错误,标签不支持特定错误代码"; case 0x10: return "有字节空间被锁定,写入失败"; case 0x11: return "不能锁定"; case 0x12: return "已经锁定,不能再次锁定"; case 0x13: return "参数保存失败,但设置的值在读写模块断电前有效"; case 0x14: return "无法调整"; case 0x15: return "询查时间结束前返回"; case 0x17: return "本条消息之后,还有消息"; case 0x18: return "读写模块存储空间已满"; case 0x19: return "电子不支持该命令或者访问密码不能为0"; case 0x30: return "通讯错误"; case 0x33: return "通讯繁忙,设备正在执行其他命令"; case 0x35: return "端口已打开"; case 0x37: return "无效句柄"; case 0xF8: return "天线检测错误"; case 0xF9: return "命令执行出错"; case 0xFA: return "有电子标签,但通信不畅,无法操作"; case 0xFB: return "无电子标签可操作"; case 0xFC: return "电子标签返回错误代码"; case 0xFD: return "命令长度错误"; case 0xFE: return "不合法的命令"; case 0xFF: return "参数错误"; default: return "未知错误码"; } } // ==================== 标签信息类 ==================== /** * 标签信息类 */ public static class TagInfo { public String epc; // EPC(十六进制字符串) public int rssi; // RSSI(dBm) public int antenna; // 天线号 public long timestamp; // 时间戳 @Override public String toString() { return String.format("EPC: %s, RSSI: %ddBm, 天线: %d", epc, rssi, antenna); } } }