| | |
| | | |
| | | <build> |
| | | <finalName>wcs</finalName> |
| | | <resources> |
| | | <resource> |
| | | <directory>src/main/resources</directory> |
| | | <filtering>true</filtering> |
| | | <excludes> |
| | | <exclude>**/com_rfid_uhf288_Device.dll</exclude> |
| | | <exclude>**/UHFReader288.dll</exclude> |
| | | </excludes> |
| | | </resource> |
| | | <resource> |
| | | <directory>src/main/resources</directory> |
| | | <filtering>false</filtering> |
| | | <includes> |
| | | <include>**/com_rfid_uhf288_Device.dll</include> |
| | | <include>**/UHFReader288.dll</include> |
| | | </includes> |
| | | </resource> |
| | | </resources> |
| | | <plugins> |
| | | <plugin> |
| | | <groupId>org.springframework.boot</groupId> |
| New file |
| | |
| | | package com.rfid.uhf288; |
| | | |
| | | import java.io.File; |
| | | |
| | | /** |
| | | * RFID UHF288设备JNI接口 |
| | | * 对应DLL: com_rfid_uhf288_Device.dll |
| | | * |
| | | * 参考demo项目的加载方式:在static代码块中加载DLL |
| | | * 注意:DLL可能不支持多线程并发访问,需要同步保护 |
| | | */ |
| | | public class Device { |
| | | |
| | | // DLL加载状态标志 |
| | | private static volatile boolean libraryLoaded = false; |
| | | // DLL加载锁,确保只加载一次 |
| | | private static final Object loadLock = new Object(); |
| | | |
| | | /** |
| | | * 静态代码块:加载DLL库 |
| | | * 参考demo项目的方式,优先使用绝对路径加载,如果失败则尝试从系统PATH加载 |
| | | */ |
| | | static { |
| | | loadLibrary(); |
| | | } |
| | | |
| | | /** |
| | | * 加载DLL库 |
| | | * 优先使用绝对路径直接加载,如果失败则尝试从系统PATH加载(参考demo项目的方式) |
| | | * 使用同步锁确保线程安全,避免多线程同时加载导致冲突 |
| | | */ |
| | | private static void loadLibrary() { |
| | | // 如果已经加载,直接返回 |
| | | if (libraryLoaded) { |
| | | return; |
| | | } |
| | | |
| | | // 同步加载,确保只加载一次 |
| | | synchronized (loadLock) { |
| | | // 双重检查,避免重复加载 |
| | | if (libraryLoaded) { |
| | | return; |
| | | } |
| | | |
| | | String mainDllName = "com_rfid_uhf288_Device.dll"; |
| | | String depDllName = "UHFReader288.dll"; // 依赖的DLL |
| | | |
| | | // 方法1: 使用绝对路径直接加载DLL |
| | | try { |
| | | // 使用真实路径加载DLL |
| | | String projectBaseDir = "D:\\work\\work-zy\\zy-wcs"; |
| | | String mainDllPath = projectBaseDir + "\\src\\main\\resources\\lib\\" + mainDllName; |
| | | String depDllPath = projectBaseDir + "\\src\\main\\resources\\lib\\" + depDllName; |
| | | |
| | | File mainDllFile = new File(mainDllPath); |
| | | File depDllFile = new File(depDllPath); |
| | | |
| | | if (mainDllFile.exists() && mainDllFile.canRead()) { |
| | | // 先尝试加载依赖DLL(如果存在) |
| | | if (depDllFile.exists() && depDllFile.canRead()) { |
| | | try { |
| | | System.load(depDllPath); |
| | | System.out.println("成功加载依赖DLL: " + depDllName + " (路径: " + depDllPath + ")"); |
| | | } catch (UnsatisfiedLinkError e) { |
| | | // 依赖DLL可能已经加载过,或者加载失败 |
| | | // 如果错误信息不是"已加载",则打印警告 |
| | | if (e.getMessage() == null || !e.getMessage().contains("already loaded")) { |
| | | System.err.println("警告: 依赖DLL加载失败(继续尝试加载主DLL): " + depDllName); |
| | | System.err.println("错误信息: " + e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 加载主DLL |
| | | System.load(mainDllPath); |
| | | System.out.println("成功从绝对路径加载DLL: " + mainDllName + " (路径: " + mainDllPath + ")"); |
| | | System.out.println("DLL文件大小: " + mainDllFile.length() + " 字节"); |
| | | libraryLoaded = true; |
| | | return; |
| | | } else { |
| | | System.err.println("DLL文件不存在或不可读: " + mainDllPath); |
| | | } |
| | | } catch (UnsatisfiedLinkError e) { |
| | | // 如果是依赖库错误,提供更详细的错误信息 |
| | | if (e.getMessage() != null && e.getMessage().contains("dependent libraries")) { |
| | | System.err.println("DLL依赖库加载失败: " + e.getMessage()); |
| | | System.err.println("请确保所有依赖DLL文件都在同一目录中"); |
| | | } |
| | | System.err.println("从绝对路径加载DLL失败: " + e.getMessage()); |
| | | e.printStackTrace(); |
| | | } catch (Exception e) { |
| | | System.err.println("从绝对路径加载DLL失败: " + e.getMessage()); |
| | | e.printStackTrace(); |
| | | } |
| | | |
| | | /* 方法2(已注释): 尝试从resources目录提取到临时目录加载 |
| | | try { |
| | | // 创建专门的临时目录来存放所有DLL(只创建一次) |
| | | if (tempLibDir == null) { |
| | | tempLibDir = createTempLibDirectory(); |
| | | } |
| | | |
| | | if (tempLibDir != null && tempLibDir.exists()) { |
| | | // 先提取所有DLL到同一目录 |
| | | File depDll = extractDllToDir("/lib/" + depDllName, depDllName, tempLibDir); |
| | | File mainDll = extractDllToDir("/lib/" + mainDllName, mainDllName, tempLibDir); |
| | | |
| | | if (mainDll != null && mainDll.exists()) { |
| | | // 先尝试加载依赖DLL(如果存在) |
| | | if (depDll != null && depDll.exists()) { |
| | | try { |
| | | System.load(depDll.getAbsolutePath()); |
| | | System.out.println("成功加载依赖DLL: " + depDllName + " (路径: " + depDll.getAbsolutePath() + ")"); |
| | | } catch (UnsatisfiedLinkError e) { |
| | | if (e.getMessage() == null || !e.getMessage().contains("already loaded")) { |
| | | System.err.println("警告: 依赖DLL加载失败(继续尝试加载主DLL): " + depDllName); |
| | | System.err.println("错误信息: " + e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | System.load(mainDll.getAbsolutePath()); |
| | | System.out.println("成功从resources目录加载DLL: " + mainDllName + " (路径: " + mainDll.getAbsolutePath() + ")"); |
| | | return; |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | System.err.println("从resources目录加载DLL失败: " + e.getMessage()); |
| | | e.printStackTrace(); |
| | | } |
| | | */ |
| | | |
| | | // 方法3: 尝试从系统PATH或java.library.path加载(参考demo项目的方式) |
| | | try { |
| | | System.loadLibrary("com_rfid_uhf288_Device"); |
| | | System.out.println("成功从系统PATH加载DLL: " + mainDllName); |
| | | System.out.println("注意:如果后续native方法调用失败,请检查DLL版本是否匹配"); |
| | | libraryLoaded = true; |
| | | return; |
| | | } catch (UnsatisfiedLinkError e) { |
| | | System.err.println("无法从系统PATH加载RFID设备DLL库: " + mainDllName); |
| | | System.err.println("错误信息: " + e.getMessage()); |
| | | System.err.println("请确保DLL文件在系统PATH或项目lib目录中"); |
| | | e.printStackTrace(); |
| | | } |
| | | |
| | | // 如果所有方法都失败,记录错误 |
| | | if (!libraryLoaded) { |
| | | System.err.println("========================================"); |
| | | System.err.println("RFID设备DLL加载失败!"); |
| | | System.err.println("请检查:"); |
| | | System.err.println("1. DLL文件是否存在: D:\\work\\work-zy\\zy-wcs\\src\\main\\resources\\lib\\com_rfid_uhf288_Device.dll"); |
| | | System.err.println("2. DLL文件是否完整"); |
| | | System.err.println("3. DLL文件是否在系统PATH中"); |
| | | System.err.println("4. 从demo项目中复制正确的DLL文件"); |
| | | System.err.println("========================================"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * @param args |
| | | */ |
| | | public native int OpenComPort(int port,byte[]comAddr,byte baud,int[]PortHandle); |
| | | public native int CloseSpecComPort(int PortHandle); |
| | | public native int OpenNetPort(int Port,String IPaddr,byte[]comAddr,int[] PortHandle); |
| | | public native int CloseNetPort(int PortHandle); |
| | | public native int GetReaderInformation(byte[]comAddr,byte[]versionInfo,byte[]readerType,byte[]trType,byte[]dmaxfre, |
| | | byte[]dminfre,byte[]powerdBm,byte[]InventoryScanTime,byte[]Ant,byte[]BeepEn, |
| | | byte[]OutputRep,byte[]CheckAnt,int PortHandle); |
| | | |
| | | public native int Inventory_G2(byte[]comAddr,byte QValue,byte Session,byte MaskMem,byte[]MaskAdr,byte MaskLen, |
| | | byte[]MaskData,byte MaskFlag,byte AdrTID,byte LenTID,byte TIDFlag,byte Target, |
| | | byte InAnt,byte Scantime,byte FastFlag,byte[]pEPCList,byte[] Ant,int[]Totallen, |
| | | int[]CardNum,int PortHandle); |
| | | |
| | | public native int InventoryMix_G2(byte[]comAddr,byte QValue,byte Session,byte MaskMem,byte[]MaskAdr,byte MaskLen, |
| | | byte[]MaskData,byte MaskFlag,byte ReadMem,byte[]ReadAdr,byte ReadLen,byte[]Psd, |
| | | byte Target,byte InAnt,byte Scantime,byte FastFlag,byte[]pEPCList,byte[] Ant, |
| | | int[]Totallen,int[]CardNum,int PortHandle); |
| | | |
| | | public native int ReadData_G2(byte[]comAddr,byte[] EPC,byte ENum,byte Mem,byte WordPtr,byte Num,byte[]Password, |
| | | byte MaskMem,byte[]MaskAdr,byte MaskLen,byte[]MaskData,byte[]Data,int[]Errorcode, |
| | | int PortHandle); |
| | | |
| | | public native int WriteData_G2(byte[]comAddr,byte[] EPC,byte WNum,byte ENum,byte Mem,byte WordPtr,byte[] Wdt, |
| | | byte[]Password,byte MaskMem,byte[]MaskAdr,byte MaskLen,byte[]MaskData,int[]Errorcode, |
| | | int PortHandle); |
| | | |
| | | public native int WriteEPC_G2(byte[]comAddr,byte[] Password,byte[] EPC,byte ENum,int[]Errorcode,int PortHandle); |
| | | |
| | | public native int KillTag_G2(byte[]comAddr,byte[] EPC,byte ENum,byte[] Killpwd,byte MaskMem,byte[]MaskAdr,byte MaskLen, |
| | | byte[]MaskData,int[]Errorcode,int PortHandle); |
| | | public native int Lock_G2(byte[]comAddr,byte[] EPC,byte ENum,byte selectid,byte setprotect,byte[] Password,byte MaskMem, |
| | | byte[]MaskAdr,byte MaskLen, byte[]MaskData,int[]Errorcode,int PortHandle); |
| | | public native int BlockErase_G2(byte[]comAddr,byte[] EPC,byte ENum,byte Mem,byte WordPtr,byte Num,byte[] Password, |
| | | byte MaskMem,byte[]MaskAdr,byte MaskLen, byte[]MaskData,int[]Errorcode,int PortHandle); |
| | | public native int SetRegion(byte[]comAddr, byte dmaxfre,byte dminfre,int PortHandle); |
| | | public native int SetAddress(byte[]comAddr, byte ComAdrData,int PortHandle); |
| | | public native int SetInventoryScanTime(byte[]comAddr, byte ScanTime,int PortHandle); |
| | | public native int SetBaudRate(byte[]comAddr, byte baud,int PortHandle); |
| | | public native int SetRfPower(byte[]comAddr, byte PowerDbm,int PortHandle); |
| | | public native int SetWorkMode(byte[]comAddr, byte Read_mode,int PortHandle); |
| | | public native int GetSystemParameter(byte[]comAddr,byte[]Read_mode,byte[]Accuracy,byte[]RepCondition,byte[]RepPauseTime, |
| | | byte[]ReadPauseTim,byte[]TagProtocol,byte[]MaskMem,byte[]MaskAdr,byte[]MaskLen, |
| | | byte[]MaskData, byte[]TriggerTime,byte[]AdrTID,byte[]LenTID,int PortHandle); |
| | | public native int SetAntennaMultiplexing(byte[]comAddr, byte Ant,int PortHandle); |
| | | public native int SetAntenna(byte[]comAddr, byte SetOnce,byte AntCfg1,byte AntCfg2,int PortHandle); |
| | | public native int WriteRfPower(byte[]comAddr, byte PowerDbm,int PortHandle); |
| | | public native int ReadRfPower(byte[]comAddr, byte[] PowerDbm,int PortHandle); |
| | | public native int RetryTimes(byte[]comAddr, byte[] Times,int PortHandle); |
| | | public native int SetReadMode(byte[]comAddr, byte ReadMode,int PortHandle); |
| | | public native int SetCheckAnt(byte[]comAddr, byte CheckAnt,int PortHandle); |
| | | public native int GetSeriaNo(byte[]comAddr, byte[] SeriaNo,int PortHandle); |
| | | public native int SetBeepNotification(byte[]comAddr, byte BeepEn,int PortHandle); |
| | | public native int SetReal_timeClock(byte[]comAddr, byte[] paramer,int PortHandle); |
| | | public native int GetTime(byte[]comAddr, byte[] paramer,int PortHandle); |
| | | public native int SetDRM(byte[]comAddr, byte DRM,int PortHandle); |
| | | public native int GetDRM(byte[]comAddr, byte[] DRM,int PortHandle); |
| | | public native int GetReaderTemperature(byte[]comAddr, byte[] PlusMinus,byte[]Temperature,int PortHandle); |
| | | public native int MeasureReturnLoss(byte[]comAddr, byte[] TestFreq,byte Ant,byte[]ReturnLoss,int PortHandle); |
| | | public native int SetGPIO(byte[] comAddr, byte OutputPin, int PortHandle); |
| | | public native int GetGPIOStatus(byte[] ComAdr, byte[] OutputPin, int PortHandle); |
| | | public native int ReadActiveModeData(byte[] ScanModeData, int[] ValidDatalength, int PortHandle); |
| | | public native int SetRelay(byte[] comAddr, byte RelayTime, int PortHandle); |
| | | public native int SwitchProtocol(byte[] comAddr, byte[] protocol, int PortHandle); |
| | | |
| | | public native int Inventory_JB(byte[]comAddr,byte Algo,byte MaskMem,byte[]MaskAdr,byte MaskLen, |
| | | byte[]MaskData,byte MaskFlag, byte InAnt,byte Scantime,byte FastFlag,byte[]pEPCList,byte[] Ant,int[]Totallen, |
| | | int[]CardNum,int PortHandle); |
| | | |
| | | public native int InventoryMix_JB(byte[]comAddr,byte Algo,byte MaskMem,byte[]MaskAdr,byte MaskLen, |
| | | byte[]MaskData,byte MaskFlag,byte ReadMem,byte[]ReadAdr,byte ReadLen,byte[]Psd, |
| | | byte InAnt,byte Scantime,byte FastFlag,byte[]pEPCList,byte[] Ant, |
| | | int[]Totallen,int[]CardNum,int PortHandle); |
| | | |
| | | public native int ReadData_JB(byte[]comAddr,byte[] TagID,byte TNum,byte Mem,int WordPtr,byte WordNum,byte[]Password, |
| | | byte MaskMem,byte[]MaskAdr,byte MaskLen,byte[]MaskData,byte[]Data,int[]Errorcode, |
| | | int PortHandle); |
| | | |
| | | public native int WriteData_JB(byte[]comAddr,byte[] TagID,byte WNum,byte TNum,byte Mem,int WordPtr,byte[] Wdt, |
| | | byte[]Password,byte MaskMem,byte[]MaskAdr,byte MaskLen,byte[]MaskData,int[]Errorcode, |
| | | int PortHandle); |
| | | public native int KillTag_JB(byte[]comAddr,byte[] TagID,byte TNum,byte[] Killpwd,byte MaskMem,byte[]MaskAdr,byte MaskLen, |
| | | byte[]MaskData,int[]Errorcode,int PortHandle); |
| | | public native int Lock_JB(byte[]comAddr,byte[] EPC,byte TNum,byte LockMem,byte Cfg,byte Action,byte[] Password,byte MaskMem, |
| | | byte[]MaskAdr,byte MaskLen, byte[]MaskData,int[]Errorcode,int PortHandle); |
| | | public native int BlockErase_JB(byte[]comAddr,byte[] EPC,byte TNum,byte Mem,int WordPtr,int Num,byte[] Password, |
| | | byte MaskMem,byte[]MaskAdr,byte MaskLen, byte[]MaskData,int[]Errorcode,int PortHandle); |
| | | |
| | | |
| | | public native int SetCfgParameter(byte[] comAddr, byte opt, byte cfgNum, byte[]data,int len, int FrmHandle); |
| | | |
| | | public native int GetCfgParameter (byte[] comAddr, byte cfgNum,byte[] data, int[] len, int FrmHandle); |
| | | |
| | | public native int SetProfile(byte[] comAddr, byte[] Profile, int FrmHandle); |
| | | public native int SetExtProfile(byte[] comAddr,byte Opt, int[] Profile, int FrmHandle); |
| | | |
| | | public native int GetEx10Version(byte[] comAddr,byte[] Version, byte[] ex10Tpye, int FrmHandle); |
| | | |
| | | public static void main(String[] args) { |
| | | // TODO Auto-generated method stub |
| | | |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | 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<TagInfo> readTags(int qValue, int session, int scanTime) { |
| | | List<TagInfo> 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<TagInfo> 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); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.controller; |
| | | |
| | | import com.core.common.R; |
| | | import com.zy.service.RFIDService; |
| | | import com.zy.service.RFIDAutoConnectService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * RFID设备HTTP API接口 |
| | | * 提供RFID设备连接、检测等功能 |
| | | * Created on 2026/01/10 |
| | | */ |
| | | @Slf4j |
| | | @RestController |
| | | @RequestMapping("/api/rfid") |
| | | public class RFIDController { |
| | | |
| | | @Autowired |
| | | private RFIDService rfidService; |
| | | |
| | | @Autowired |
| | | private RFIDAutoConnectService rfidAutoConnectService; |
| | | |
| | | /** |
| | | * 网络连接RFID设备 |
| | | * |
| | | * POST /api/rfid/connect/network |
| | | * Body: {"ip": "10.10.10.210", "port": 27011} |
| | | * |
| | | * @param params 连接参数 |
| | | * @return 连接结果 |
| | | */ |
| | | @PostMapping("/connect/network") |
| | | public R connectNetwork(@RequestBody Map<String, Object> params) { |
| | | String ip = (String) params.get("ip"); |
| | | Integer port = params.get("port") != null ? |
| | | Integer.valueOf(params.get("port").toString()) : null; |
| | | |
| | | if (ip == null || ip.isEmpty()) { |
| | | return R.error("IP地址不能为空"); |
| | | } |
| | | |
| | | Map<String, Object> result = rfidService.connectNetwork(ip, port); |
| | | return result.get("success").equals(true) ? R.ok(result) : R.error(result.get("message").toString()); |
| | | } |
| | | |
| | | /** |
| | | * 串口连接RFID设备 |
| | | * |
| | | * POST /api/rfid/connect/comport |
| | | * Body: {"port": 3, "baud": 3} |
| | | * |
| | | * @param params 连接参数 |
| | | * @return 连接结果 |
| | | */ |
| | | @PostMapping("/connect/comport") |
| | | public R connectComPort(@RequestBody Map<String, Object> params) { |
| | | Integer port = params.get("port") != null ? |
| | | Integer.valueOf(params.get("port").toString()) : null; |
| | | Integer baud = params.get("baud") != null ? |
| | | Integer.valueOf(params.get("baud").toString()) : null; |
| | | |
| | | Map<String, Object> result = rfidService.connectComPort(port, baud); |
| | | return result.get("success").equals(true) ? R.ok(result) : R.error(result.get("message").toString()); |
| | | } |
| | | |
| | | /** |
| | | * 检测RFID标签(单次检测) |
| | | * |
| | | * GET /api/rfid/detect |
| | | * GET /api/rfid/detect?scanTime=5 |
| | | * |
| | | * @param scanTime 扫描时间(单位: 100ms,默认5) |
| | | * @return 检测结果 |
| | | */ |
| | | @GetMapping("/detect") |
| | | public R detectTags(@RequestParam(required = false) Integer scanTime) { |
| | | Map<String, Object> result = rfidService.detectTags(scanTime); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | /** |
| | | * 检测RFID标签(POST方式,可传递更多参数) |
| | | * |
| | | * POST /api/rfid/detect |
| | | * Body: {"scanTime": 5} |
| | | * |
| | | * @param params 检测参数 |
| | | * @return 检测结果 |
| | | */ |
| | | @PostMapping("/detect") |
| | | public R detectTagsPost(@RequestBody(required = false) Map<String, Object> params) { |
| | | Integer scanTime = null; |
| | | if (params != null && params.get("scanTime") != null) { |
| | | scanTime = Integer.valueOf(params.get("scanTime").toString()); |
| | | } |
| | | |
| | | Map<String, Object> result = rfidService.detectTags(scanTime); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | /** |
| | | * 断开RFID设备连接 |
| | | * |
| | | * POST /api/rfid/disconnect |
| | | * |
| | | * @return 断开结果 |
| | | */ |
| | | @PostMapping("/disconnect") |
| | | public R disconnect() { |
| | | Map<String, Object> result = rfidService.disconnect(); |
| | | return result.get("success").equals(true) ? R.ok(result) : R.error(result.get("message").toString()); |
| | | } |
| | | |
| | | /** |
| | | * 获取连接状态 |
| | | * |
| | | * GET /api/rfid/status |
| | | * |
| | | * @return 连接状态 |
| | | */ |
| | | @GetMapping("/status") |
| | | public R getStatus() { |
| | | Map<String, Object> result = rfidService.getConnectionStatus(); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | /** |
| | | * 诊断DLL加载状态 |
| | | * |
| | | * GET /api/rfid/diagnose |
| | | * |
| | | * @return 诊断信息 |
| | | */ |
| | | @GetMapping("/diagnose") |
| | | public R diagnose() { |
| | | Map<String, Object> result = rfidService.diagnoseDll(); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | /** |
| | | * 获取所有RFID自动连接设备状态 |
| | | * |
| | | * GET /api/rfid/auto-connect/status |
| | | * |
| | | * @return 所有设备状态 |
| | | */ |
| | | @GetMapping("/auto-connect/status") |
| | | public R getAutoConnectStatus() { |
| | | return R.ok(rfidAutoConnectService.getAllDeviceStatus()); |
| | | } |
| | | |
| | | /** |
| | | * 获取指定RFID自动连接设备状态 |
| | | * |
| | | * GET /api/rfid/auto-connect/status/{deviceId} |
| | | * |
| | | * @param deviceId 设备ID |
| | | * @return 设备状态 |
| | | */ |
| | | @GetMapping("/auto-connect/status/{deviceId}") |
| | | public R getAutoConnectDeviceStatus(@PathVariable Integer deviceId) { |
| | | Map<String, Object> result = rfidAutoConnectService.getDeviceStatus(deviceId); |
| | | return R.ok(result); |
| | | } |
| | | |
| | | /** |
| | | * 手动启动指定RFID设备自动连接 |
| | | * |
| | | * POST /api/rfid/auto-connect/start/{deviceId} |
| | | * |
| | | * @param deviceId 设备ID |
| | | * @return 启动结果 |
| | | */ |
| | | @PostMapping("/auto-connect/start/{deviceId}") |
| | | public R startAutoConnect(@PathVariable Integer deviceId) { |
| | | boolean success = rfidAutoConnectService.startDevice(deviceId); |
| | | if (success) { |
| | | return R.ok("RFID设备[" + deviceId + "]自动连接已启动"); |
| | | } else { |
| | | return R.error("RFID设备[" + deviceId + "]启动失败,请检查配置或设备是否已在运行"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 手动停止指定RFID设备自动连接 |
| | | * |
| | | * POST /api/rfid/auto-connect/stop/{deviceId} |
| | | * |
| | | * @param deviceId 设备ID |
| | | * @return 停止结果 |
| | | */ |
| | | @PostMapping("/auto-connect/stop/{deviceId}") |
| | | public R stopAutoConnect(@PathVariable Integer deviceId) { |
| | | boolean success = rfidAutoConnectService.stopDevice(deviceId); |
| | | if (success) { |
| | | return R.ok("RFID设备[" + deviceId + "]自动连接已停止"); |
| | | } else { |
| | | return R.error("RFID设备[" + deviceId + "]停止失败,设备可能未启用自动连接"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 手动停止所有RFID设备自动连接 |
| | | * |
| | | * POST /api/rfid/auto-connect/stop-all |
| | | * |
| | | * @return 停止结果 |
| | | */ |
| | | @PostMapping("/auto-connect/stop-all") |
| | | public R stopAllAutoConnect() { |
| | | rfidAutoConnectService.stopAll(); |
| | | return R.ok("所有RFID设备自动连接已停止"); |
| | | } |
| | | |
| | | /** |
| | | * 重新配置指定RFID设备的天线(用于天线插拔后重新配置) |
| | | * |
| | | * POST /api/rfid/reconfigure-antenna/{deviceId} |
| | | * |
| | | * @param deviceId 设备ID |
| | | * @return 配置结果 |
| | | */ |
| | | @PostMapping("/reconfigure-antenna/{deviceId}") |
| | | public R reconfigureAntenna(@PathVariable Integer deviceId) { |
| | | Map<String, Object> deviceStatus = rfidAutoConnectService.getDeviceStatus(deviceId); |
| | | if (!Boolean.TRUE.equals(deviceStatus.get("exists"))) { |
| | | return R.error("RFID设备[" + deviceId + "]未启用自动连接或不存在"); |
| | | } |
| | | |
| | | if (!Boolean.TRUE.equals(deviceStatus.get("templateConnected"))) { |
| | | return R.error("RFID设备[" + deviceId + "]未连接,无法配置天线"); |
| | | } |
| | | |
| | | // 获取设备管理器并重新配置天线 |
| | | boolean success = rfidAutoConnectService.reconfigureAntenna(deviceId); |
| | | if (success) { |
| | | return R.ok("RFID设备[" + deviceId + "]天线重新配置成功"); |
| | | } else { |
| | | return R.error("RFID设备[" + deviceId + "]天线重新配置失败,请查看日志"); |
| | | } |
| | | } |
| | | |
| | | @GetMapping("/tags") |
| | | public R getTags(@RequestParam(required = false) Integer deviceId, |
| | | @RequestParam(required = false) String ip, |
| | | @RequestParam(required = false) Integer antenna) { |
| | | if (deviceId == null && (ip == null || ip.isEmpty())) { |
| | | return R.error("请提供设备ID或IP地址"); |
| | | } |
| | | List<Map<String, Object>> tags = rfidAutoConnectService.getDeviceTags(deviceId, ip, antenna); |
| | | return R.ok(tags); |
| | | } |
| | | |
| | | @GetMapping("/query-tags") |
| | | public R queryTags(@RequestParam(required = false) Integer deviceId, |
| | | @RequestParam(required = false) String ip, |
| | | @RequestParam(required = false) Integer antenna) { |
| | | if (deviceId == null && (ip == null || ip.isEmpty())) { |
| | | return R.error("请提供设备ID或IP地址"); |
| | | } |
| | | List<Map<String, Object>> tags = rfidAutoConnectService.queryDeviceTags(deviceId, ip, antenna); |
| | | return R.ok(tags); |
| | | } |
| | | } |
| | |
| | | import com.zy.core.model.CrnSlave; |
| | | import com.zy.core.model.DevpSlave; |
| | | import com.zy.core.model.LedSlave; |
| | | import com.zy.core.model.RFIDSlave; |
| | | import com.zy.core.properties.SlaveProperties; |
| | | import com.zy.core.thread.BarcodeThread; |
| | | import com.zy.core.thread.LedThread; |
| | | import com.zy.core.thread.RFIDThread; |
| | | import com.zy.core.thread.SiemensCrnThread; |
| | | import com.zy.core.thread.SiemensDevpThread; |
| | | import com.zy.utils.News; |
| | |
| | | for (Slave led : slaveProperties.getLed()) { |
| | | MessageQueue.init(SlaveType.Led, led); |
| | | } |
| | | // 初始化RFID读写器mq |
| | | for (Slave rfid : slaveProperties.getRfid()) { |
| | | MessageQueue.init(SlaveType.Rfid, rfid); |
| | | } |
| | | // 初始化磅称mq |
| | | // for (Slave scale : slaveProperties.getScale()) { |
| | | // MessageQueue.init(SlaveType.Scale, scale); |
| | |
| | | new Thread(ledThread).start(); |
| | | SlaveConnection.put(SlaveType.Led, led.getId(), ledThread); |
| | | } |
| | | // 初始化RFID读写器线程 |
| | | // 注意:如果RFID设备配置了autoConnect=true,将由RFIDAutoConnectService管理,这里跳过 |
| | | News.info("初始化RFID读写器线程..................................................."); |
| | | for (RFIDSlave rfid : slaveProperties.getRfid()) { |
| | | // 如果启用了自动连接,跳过RFIDThread的创建(由RFIDAutoConnectService管理) |
| | | if (rfid.getAutoConnect() != null && rfid.getAutoConnect()) { |
| | | News.info("RFID设备[{}]已启用自动连接,跳过RFIDThread初始化", rfid.getId()); |
| | | continue; |
| | | } |
| | | RFIDThread rfidThread = new RFIDThread(rfid); |
| | | new Thread(rfidThread).start(); |
| | | SlaveConnection.put(SlaveType.Rfid, rfid.getId(), rfidThread); |
| | | } |
| | | // 初始化磅秤线程 |
| | | // News.info("初始化磅秤线程..................................................."); |
| | | // for (Slave scale : slaveProperties.getScale()) { |
| | |
| | | public static ArrayBlockingQueue<JSONObject> BARCODE = new ArrayBlockingQueue<>(32); |
| | | // rgv输出日志 |
| | | public static ArrayBlockingQueue<String> RGV = new ArrayBlockingQueue<>(32); |
| | | // RFID输出日志 |
| | | public static ArrayBlockingQueue<JSONObject> RFID = new ArrayBlockingQueue<>(32); |
| | | } |
| | |
| | | Led, |
| | | Scale, |
| | | Car, |
| | | Rgv |
| | | Rgv, |
| | | Rfid |
| | | ; |
| | | |
| | | public static SlaveType findInstance(String s){ |
| New file |
| | |
| | | package com.zy.core.model; |
| | | |
| | | import com.zy.core.Slave; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | /** |
| | | * RFID设备配置 |
| | | * Created on 2026/01/10 |
| | | */ |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @Data |
| | | public class RFIDSlave extends Slave { |
| | | |
| | | /** |
| | | * 是否自动连接(true: 系统启动时自动连接,false: 手动连接) |
| | | */ |
| | | private Boolean autoConnect = false; |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.core.thread; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.core.common.Cools; |
| | | import com.core.common.DateUtils; |
| | | import com.zy.core.Slave; |
| | | import com.zy.core.ThreadHandler; |
| | | import com.zy.core.cache.OutputQueue; |
| | | import com.rfid.uhf288.Device; |
| | | import com.zy.utils.News; |
| | | import lombok.Data; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import java.text.MessageFormat; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * RFID读写器线程 |
| | | * 基于UHF288设备,支持串口和网络连接 |
| | | * Created by chen.lin on 2026/01/10 |
| | | */ |
| | | @Data |
| | | @Slf4j |
| | | public class RFIDThread implements Runnable, ThreadHandler { |
| | | |
| | | private Slave slave; |
| | | private StringBuffer rfid = new StringBuffer(); |
| | | private String lastRfid = ""; |
| | | |
| | | private Device reader; |
| | | private int portHandle = -1; |
| | | private byte[] comAddr = new byte[1]; |
| | | private boolean isConnected = false; |
| | | private String connectionType = ""; // "COM" 或 "NET" |
| | | |
| | | // 连接参数 |
| | | private Integer comPort; // 串口号(如果使用串口) |
| | | private Byte baudRate; // 波特率(如果使用串口) |
| | | private String ipAddress; // IP地址(如果使用网络) |
| | | private Integer netPort; // 网络端口(如果使用网络) |
| | | |
| | | public RFIDThread(Slave slave) { |
| | | this.slave = slave; |
| | | try { |
| | | reader = new Device(); |
| | | comAddr[0] = (byte)255; // 默认地址0xFF |
| | | } catch (Exception e) { |
| | | log.error("RFID设备初始化失败", e); |
| | | } |
| | | } |
| | | |
| | | public String getRfid() { |
| | | return rfid.toString(); |
| | | } |
| | | |
| | | public void setRfid(String rfid) { |
| | | this.rfid.delete(0, this.rfid.length()); |
| | | this.rfid.append(rfid); |
| | | if(!Cools.isEmpty(rfid) && !lastRfid.equals(rfid)) { |
| | | News.info("RFID"+" - 1"+" - {}号RFID读写器,检索数据:{}", slave.getId(), this.rfid); |
| | | lastRfid = rfid; |
| | | JSONObject jsonObject = new JSONObject(); |
| | | jsonObject.put("time", DateUtils.convert(new Date(), DateUtils.yyyyMMddHHmmss_F)); |
| | | jsonObject.put("rfid", rfid); |
| | | if (OutputQueue.RFID.size() >= 32) { |
| | | OutputQueue.RFID.poll(); |
| | | } |
| | | OutputQueue.RFID.offer(jsonObject); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public boolean connect() { |
| | | if (isConnected) { |
| | | log.warn("RFID设备已连接,无需重复连接"); |
| | | return true; |
| | | } |
| | | |
| | | boolean result = false; |
| | | |
| | | try { |
| | | // 优先尝试网络连接(如果配置了IP) |
| | | if (!Cools.isEmpty(slave.getIp()) && slave.getPort() != null) { |
| | | result = connectNetwork(slave.getIp(), slave.getPort()); |
| | | if (result) { |
| | | connectionType = "NET"; |
| | | JSONObject connMsg = new JSONObject(); |
| | | connMsg.put("time", DateUtils.convert(new Date(), DateUtils.yyyyMMddHHmmss_F)); |
| | | connMsg.put("message", MessageFormat.format("RFID网络连接成功 ===>> [id:{0}] [ip:{1}] [port:{2}]", |
| | | slave.getId(), slave.getIp(), slave.getPort())); |
| | | OutputQueue.RFID.offer(connMsg); |
| | | log.info("RFID网络连接成功 ===>> [id:{}] [ip:{}] [port:{}]", slave.getId(), slave.getIp(), slave.getPort()); |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | // 如果网络连接失败,尝试串口连接 |
| | | // 注意:串口连接需要额外的配置参数(串口号和波特率) |
| | | // 这里假设可以通过某种方式获取,或者使用默认值 |
| | | if (comPort == null) { |
| | | comPort = 3; // 默认COM3 |
| | | } |
| | | if (baudRate == null) { |
| | | baudRate = (byte)3; // 默认57600bps |
| | | } |
| | | |
| | | result = connectComPort(comPort, baudRate); |
| | | if (result) { |
| | | connectionType = "COM"; |
| | | JSONObject connMsg = new JSONObject(); |
| | | connMsg.put("time", DateUtils.convert(new Date(), DateUtils.yyyyMMddHHmmss_F)); |
| | | connMsg.put("message", MessageFormat.format("RFID串口连接成功 ===>> [id:{0}] [port:COM{1}] [baud:{2}]", |
| | | slave.getId(), comPort, getBaudRateName(baudRate))); |
| | | OutputQueue.RFID.offer(connMsg); |
| | | log.info("RFID串口连接成功 ===>> [id:{}] [port:COM{}] [baud:{}]", slave.getId(), comPort, getBaudRateName(baudRate)); |
| | | return true; |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | log.error("RFID连接失败", e); |
| | | JSONObject connMsg = new JSONObject(); |
| | | connMsg.put("time", DateUtils.convert(new Date(), DateUtils.yyyyMMddHHmmss_F)); |
| | | connMsg.put("message", MessageFormat.format("RFID连接失败!!! ===>> [id:{0}] [ip:{1}] [port:{2}]", |
| | | slave.getId(), slave.getIp(), slave.getPort())); |
| | | OutputQueue.RFID.offer(connMsg); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * 串口连接 |
| | | */ |
| | | private boolean connectComPort(int port, byte baud) { |
| | | try { |
| | | int[] portHandleArray = new int[1]; |
| | | int result = reader.OpenComPort(port, comAddr, baud, portHandleArray); |
| | | |
| | | if (result == 0) { |
| | | portHandle = portHandleArray[0]; |
| | | isConnected = true; |
| | | return true; |
| | | } else { |
| | | log.error("RFID串口连接失败,错误码: {}", result); |
| | | return false; |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("RFID串口连接异常", e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 网络连接 |
| | | */ |
| | | private boolean connectNetwork(String ipAddr, int port) { |
| | | try { |
| | | int[] portHandleArray = new int[1]; |
| | | int result = reader.OpenNetPort(port, ipAddr, comAddr, portHandleArray); |
| | | |
| | | if (result == 0) { |
| | | portHandle = portHandleArray[0]; |
| | | isConnected = true; |
| | | return true; |
| | | } else { |
| | | log.error("RFID网络连接失败,错误码: {}", result); |
| | | return false; |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("RFID网络连接异常", e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 读取RFID标签 |
| | | */ |
| | | private void readTags() { |
| | | if (!isConnected || portHandle < 0) { |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | byte qValue = 4; // Q值,推荐4 |
| | | byte session = 0; // Session值,推荐0 |
| | | byte scanTime = 10; // 扫描时间,10*100ms=1秒 |
| | | byte maskMem = 2; // EPC区 |
| | | 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; // 0-读取EPC,1-读取TID |
| | | byte target = 0; |
| | | byte inAnt = (byte)0x81; |
| | | byte fastFlag = 0; |
| | | byte[] pEPCList = new byte[20000]; |
| | | byte[] ant = new byte[1]; |
| | | int[] totallen = new int[1]; |
| | | int[] cardNum = new int[1]; |
| | | |
| | | // 清空数组 |
| | | java.util.Arrays.fill(pEPCList, (byte)0); |
| | | totallen[0] = 0; |
| | | cardNum[0] = 0; |
| | | |
| | | int result = reader.Inventory_G2(comAddr, qValue, session, maskMem, maskAdr, maskLen, |
| | | maskData, maskFlag, adrTID, lenTID, tidFlag, target, inAnt, scanTime, |
| | | fastFlag, pEPCList, ant, totallen, cardNum, portHandle); |
| | | |
| | | // 解析标签数据 |
| | | if (cardNum[0] > 0 && totallen[0] > 0) { |
| | | int offset = 0; |
| | | for (int i = 0; i < cardNum[0]; i++) { |
| | | if (offset + 1 >= totallen[0]) break; |
| | | |
| | | byte epcLen = pEPCList[offset]; |
| | | offset++; |
| | | |
| | | if (offset + epcLen > totallen[0]) break; |
| | | |
| | | byte[] epcBytes = new byte[epcLen]; |
| | | System.arraycopy(pEPCList, offset, epcBytes, 0, epcLen); |
| | | offset += epcLen; |
| | | |
| | | // RSSI和天线信息 |
| | | byte rssi = 0; |
| | | byte antenna = 0; |
| | | if (offset < totallen[0]) { |
| | | rssi = pEPCList[offset]; |
| | | offset++; |
| | | } |
| | | if (offset < totallen[0]) { |
| | | antenna = pEPCList[offset]; |
| | | offset++; |
| | | } |
| | | |
| | | String epcHex = bytesToHex(epcBytes); |
| | | // 只取EPC的一部分(类似参考项目中的处理方式) |
| | | if (epcHex.length() >= 24) { |
| | | epcHex = epcHex.substring(14, 24); |
| | | } |
| | | |
| | | setRfid(epcHex); |
| | | |
| | | // 只处理第一个标签 |
| | | break; |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("RFID读取标签异常", e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 字节数组转十六进制字符串 |
| | | */ |
| | | 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 getBaudRateName(byte baud) { |
| | | String[] names = {"9600", "19200", "38400", "57600", "115200", "230400", "460800", "921600"}; |
| | | int index = baud & 0xFF; |
| | | if (index >= 0 && index < names.length) { |
| | | return names[index]; |
| | | } |
| | | return String.valueOf(baud); |
| | | } |
| | | |
| | | @Override |
| | | public void close() { |
| | | if (isConnected && portHandle >= 0) { |
| | | try { |
| | | if ("COM".equals(connectionType)) { |
| | | reader.CloseSpecComPort(portHandle); |
| | | } else if ("NET".equals(connectionType)) { |
| | | reader.CloseNetPort(portHandle); |
| | | } |
| | | isConnected = false; |
| | | portHandle = -1; |
| | | connectionType = ""; |
| | | log.info("RFID连接已关闭 ===>> [id:{}]", slave.getId()); |
| | | } catch (Exception e) { |
| | | log.error("RFID关闭连接异常", e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void run() { |
| | | connect(); |
| | | while (true) { |
| | | try { |
| | | if (isConnected) { |
| | | readTags(); |
| | | } else { |
| | | // 如果未连接,尝试重连 |
| | | connect(); |
| | | } |
| | | Thread.sleep(300); |
| | | } catch (Exception e) { |
| | | log.error("RFID线程运行异常", e); |
| | | try { |
| | | Thread.sleep(1000); |
| | | } catch (InterruptedException ie) { |
| | | Thread.currentThread().interrupt(); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.rfid; |
| | | |
| | | import com.rfid.uhf288.Device; |
| | | 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<TagInfo> readTags(int qValue, int session, int scanTime) { |
| | | List<TagInfo> 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<TagInfo> 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); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.service; |
| | | |
| | | import com.zy.core.model.RFIDSlave; |
| | | import com.zy.core.properties.SlaveProperties; |
| | | import com.rfid.uhf288.RFIDTemplate; |
| | | import com.rfid.uhf288.RFIDTemplate.TagInfo; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.PostConstruct; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | |
| | | /** |
| | | * RFID自动连接和监控服务 |
| | | * 支持多个RFID设备独立管理,每个设备独立的连接线程和检测线程 |
| | | * 每个设备的autoConnect配置独立控制 |
| | | * |
| | | * @author Created on 2026/01/10 |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class RFIDAutoConnectService { |
| | | |
| | | @Autowired |
| | | private SlaveProperties slaveProperties; |
| | | |
| | | @Value("${wcs-slave.rfid-config.reconnectInterval:5}") |
| | | private Integer globalReconnectInterval; |
| | | |
| | | @Value("${wcs-slave.rfid-config.tagScanInterval:500}") |
| | | private Integer globalTagScanInterval; |
| | | |
| | | @Value("${wcs-slave.rfid-config.tagScanTime:10}") |
| | | private Integer globalTagScanTime; |
| | | |
| | | // 存储每个设备的连接和线程信息 |
| | | private final Map<Integer, DeviceManager> deviceManagers = new ConcurrentHashMap<>(); |
| | | |
| | | /** |
| | | * 设备管理器 - 管理单个RFID设备的连接和检测 |
| | | */ |
| | | private static class DeviceManager { |
| | | final RFIDSlave config; |
| | | final RFIDTemplate template; |
| | | final AtomicBoolean running = new AtomicBoolean(false); |
| | | final AtomicBoolean connected = new AtomicBoolean(false); |
| | | Thread connectThread; |
| | | Thread scanThread; |
| | | final int reconnectInterval; |
| | | final long tagScanInterval; |
| | | final int tagScanTime; |
| | | int reconfigCounter = 0; |
| | | volatile List<TagInfo> latestTags = new ArrayList<>(); // 最新检测到的标签 |
| | | |
| | | DeviceManager(RFIDSlave config, Integer reconnectInterval, Integer tagScanInterval, Integer tagScanTime) { |
| | | this.config = config; |
| | | this.template = new RFIDTemplate(); |
| | | this.reconnectInterval = reconnectInterval != null ? reconnectInterval : 5; |
| | | this.tagScanInterval = tagScanInterval != null ? tagScanInterval : 500; |
| | | this.tagScanTime = tagScanTime != null ? tagScanTime : 10; |
| | | } |
| | | |
| | | void start() { |
| | | if (running.get()) { |
| | | return; |
| | | } |
| | | |
| | | running.set(true); |
| | | |
| | | // 启动连接线程 |
| | | // 注意:设置为非守护线程,确保应用运行期间线程一直运行 |
| | | connectThread = new Thread(this::connectLoop, |
| | | String.format("RFID-Connect-%d-%s", config.getId(), config.getIp())); |
| | | connectThread.setDaemon(false); // 改为非守护线程,确保应用运行期间一直运行 |
| | | connectThread.start(); |
| | | |
| | | // 启动标签检测线程 |
| | | scanThread = new Thread(this::scanLoop, |
| | | String.format("RFID-Scan-%d-%s", config.getId(), config.getIp())); |
| | | scanThread.setDaemon(false); // 改为非守护线程,确保应用运行期间一直运行 |
| | | scanThread.start(); |
| | | } |
| | | |
| | | void stop() { |
| | | running.set(false); |
| | | connected.set(false); |
| | | |
| | | if (connectThread != null) { |
| | | connectThread.interrupt(); |
| | | } |
| | | if (scanThread != null) { |
| | | scanThread.interrupt(); |
| | | } |
| | | |
| | | try { |
| | | if (connectThread != null) { |
| | | connectThread.join(2000); |
| | | } |
| | | if (scanThread != null) { |
| | | scanThread.join(2000); |
| | | } |
| | | } catch (InterruptedException e) { |
| | | Thread.currentThread().interrupt(); |
| | | } |
| | | |
| | | if (template != null && template.isConnected()) { |
| | | try { |
| | | template.disconnect(); |
| | | } catch (Exception e) { |
| | | log.error("RFID设备[{}]断开连接异常", config.getId(), e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 连接循环 - 负责连接和重连 |
| | | * 特别处理UnsatisfiedLinkError,确保DLL方法链接失败时也会定期重连 |
| | | */ |
| | | private void connectLoop() { |
| | | log.info("RFID设备[{}]连接线程启动 - IP: {}, Port: {}", |
| | | config.getId(), config.getIp(), config.getPort()); |
| | | |
| | | while (running.get()) { |
| | | try { |
| | | if (!connected.get()) { |
| | | log.info("========== RFID设备[{}]尝试连接 ==========", config.getId()); |
| | | log.info("IP: {}, Port: {}", config.getIp(), config.getPort()); |
| | | |
| | | // 尝试连接 |
| | | boolean success = false; |
| | | try { |
| | | success = template.connectNetwork(config.getIp(), config.getPort()); |
| | | } catch (UnsatisfiedLinkError e) { |
| | | // DLL方法链接失败,记录错误但继续重连 |
| | | log.error("RFID设备[{}] DLL方法链接失败 - UnsatisfiedLinkError", config.getId()); |
| | | log.error("错误信息: {}", e.getMessage()); |
| | | log.error("可能原因:1. DLL版本不匹配 2. 方法签名不匹配 3. DLL未正确加载"); |
| | | log.warn("将在{}秒后重试连接", reconnectInterval); |
| | | success = false; |
| | | } |
| | | |
| | | if (success) { |
| | | connected.set(true); |
| | | log.info("✓ RFID设备[{}]连接成功!IP: {}, Port: {}", |
| | | config.getId(), config.getIp(), config.getPort()); |
| | | } else { |
| | | log.warn("✗ RFID设备[{}]连接失败,{}秒后重试", |
| | | config.getId(), reconnectInterval); |
| | | Thread.sleep(reconnectInterval * 1000L); |
| | | } |
| | | } else { |
| | | // 已连接,检查连接状态 |
| | | if (!template.isConnected()) { |
| | | log.warn("RFID设备[{}]连接已断开,准备重连", config.getId()); |
| | | connected.set(false); |
| | | } else { |
| | | // 连接正常,定期重新配置天线(处理天线插拔情况) |
| | | // 每5次循环(约50秒)重新配置一次天线,确保插拔后能自动检测 |
| | | reconfigCounter++; |
| | | if (reconfigCounter >= 5) { |
| | | reconfigCounter = 0; |
| | | log.debug("RFID设备[{}]定期重新配置天线(处理天线插拔)", config.getId()); |
| | | boolean reconfigured = template.reconfigureAntenna(); |
| | | if (reconfigured) { |
| | | // log.info("RFID设备[{}]天线重新配置成功,所有天线已启用", config.getId()); |
| | | } |
| | | } |
| | | |
| | | // 等待一段时间后再次检查 |
| | | Thread.sleep(reconnectInterval * 1000L); |
| | | } |
| | | } |
| | | } catch (InterruptedException e) { |
| | | log.info("RFID设备[{}]连接线程被中断", config.getId()); |
| | | Thread.currentThread().interrupt(); |
| | | break; |
| | | } catch (UnsatisfiedLinkError e) { |
| | | // DLL方法链接失败,记录错误并继续重连 |
| | | log.error("RFID设备[{}]连接线程 - DLL方法链接失败", config.getId()); |
| | | log.error("错误信息: {}", e.getMessage()); |
| | | log.error("可能原因:1. DLL版本不匹配 2. 方法签名不匹配 3. DLL未正确加载"); |
| | | log.warn("将在{}秒后重试连接", reconnectInterval); |
| | | connected.set(false); |
| | | try { |
| | | Thread.sleep(reconnectInterval * 1000L); |
| | | } catch (InterruptedException ie) { |
| | | Thread.currentThread().interrupt(); |
| | | break; |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("RFID设备[{}]连接线程异常", config.getId(), e); |
| | | connected.set(false); |
| | | try { |
| | | Thread.sleep(reconnectInterval * 1000L); |
| | | } catch (InterruptedException ie) { |
| | | Thread.currentThread().interrupt(); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | log.info("RFID设备[{}]连接线程已停止", config.getId()); |
| | | } |
| | | |
| | | /** |
| | | * 标签检测循环 - 持续检测并打印标签 |
| | | * 特别处理UnsatisfiedLinkError,确保DLL方法链接失败时不影响重连 |
| | | */ |
| | | private void scanLoop() { |
| | | log.info("RFID设备[{}]标签检测线程启动 - IP: {}", config.getId(), config.getIp()); |
| | | |
| | | while (running.get()) { |
| | | try { |
| | | if (connected.get() && template.isConnected()) { |
| | | try { |
| | | List<TagInfo> tags = template.readTags(4, 0, tagScanTime); |
| | | latestTags = tags != null ? new ArrayList<>(tags) : new ArrayList<>(); |
| | | |
| | | if (tags != null && !tags.isEmpty()) { |
| | | printTags(tags); |
| | | } |
| | | } catch (UnsatisfiedLinkError e) { |
| | | // DLL方法链接失败,标记为未连接,让连接线程重连 |
| | | log.error("RFID设备[{}]标签检测 - DLL方法链接失败", config.getId()); |
| | | log.error("错误信息: {}", e.getMessage()); |
| | | log.warn("连接状态已标记为断开,连接线程将尝试重连"); |
| | | connected.set(false); |
| | | // 等待一段时间后继续 |
| | | Thread.sleep(reconnectInterval * 1000L); |
| | | continue; |
| | | } |
| | | } else { |
| | | // 未连接,等待一段时间 |
| | | Thread.sleep(1000); |
| | | } |
| | | |
| | | // 等待指定间隔 |
| | | Thread.sleep(tagScanInterval); |
| | | } catch (InterruptedException e) { |
| | | log.info("RFID设备[{}]标签检测线程被中断", config.getId()); |
| | | Thread.currentThread().interrupt(); |
| | | break; |
| | | } catch (UnsatisfiedLinkError e) { |
| | | // DLL方法链接失败,标记为未连接 |
| | | log.error("RFID设备[{}]标签检测线程 - DLL方法链接失败", config.getId()); |
| | | log.error("错误信息: {}", e.getMessage()); |
| | | connected.set(false); |
| | | try { |
| | | Thread.sleep(reconnectInterval * 1000L); |
| | | } catch (InterruptedException ie) { |
| | | Thread.currentThread().interrupt(); |
| | | break; |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("RFID设备[{}]标签检测线程异常", config.getId(), e); |
| | | try { |
| | | Thread.sleep(1000); |
| | | } catch (InterruptedException ie) { |
| | | Thread.currentThread().interrupt(); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | log.info("RFID设备[{}]标签检测线程已停止", config.getId()); |
| | | } |
| | | |
| | | private void printTags(List<TagInfo> tags) { |
| | | if (tags == null || tags.isEmpty()) { |
| | | return; |
| | | } |
| | | for (TagInfo tag : tags) { |
| | | log.info("RFID设备[{}]检测到标签: EPC={}, RSSI={}dBm, 天线={}", |
| | | config.getId(), tag.epc, tag.rssi, tag.antenna); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 系统启动时初始化所有配置了autoConnect=true的设备 |
| | | */ |
| | | @PostConstruct |
| | | public void init() { |
| | | log.info("========== RFID自动连接服务启动 =========="); |
| | | |
| | | if (slaveProperties.getRfid() == null || slaveProperties.getRfid().isEmpty()) { |
| | | log.info("未配置RFID设备"); |
| | | return; |
| | | } |
| | | |
| | | log.info("RFID全局配置 - 重连间隔: {}秒, 检测间隔: {}毫秒, 扫描时间: {} * 100ms", |
| | | globalReconnectInterval, globalTagScanInterval, globalTagScanTime); |
| | | |
| | | // 遍历所有RFID设备配置 |
| | | for (RFIDSlave rfidConfigItem : slaveProperties.getRfid()) { |
| | | if (rfidConfigItem == null) { |
| | | continue; |
| | | } |
| | | |
| | | // 检查是否启用自动连接 |
| | | Boolean autoConnect = rfidConfigItem.getAutoConnect(); |
| | | if (autoConnect == null || !autoConnect) { |
| | | log.info("RFID设备[{}] - IP: {}, Port: {} - 未启用自动连接", |
| | | rfidConfigItem.getId(), rfidConfigItem.getIp(), rfidConfigItem.getPort()); |
| | | continue; |
| | | } |
| | | |
| | | // 验证配置 |
| | | if (rfidConfigItem.getIp() == null || rfidConfigItem.getPort() == null) { |
| | | log.warn("RFID设备[{}]配置不完整,跳过自动连接", rfidConfigItem.getId()); |
| | | continue; |
| | | } |
| | | |
| | | // 创建设备管理器并启动 |
| | | DeviceManager manager = new DeviceManager(rfidConfigItem, |
| | | globalReconnectInterval, globalTagScanInterval, globalTagScanTime); |
| | | deviceManagers.put(rfidConfigItem.getId(), manager); |
| | | manager.start(); |
| | | |
| | | log.info("RFID设备[{}]自动连接已启动 - IP: {}, Port: {}", |
| | | rfidConfigItem.getId(), rfidConfigItem.getIp(), rfidConfigItem.getPort()); |
| | | } |
| | | |
| | | log.info("RFID自动连接服务已启动,共 {} 个设备启用自动连接", deviceManagers.size()); |
| | | } |
| | | |
| | | /** |
| | | * 手动停止所有RFID设备连接(通过API调用) |
| | | * 注意:移除了@PreDestroy,服务不会自动关闭,只能通过API手动关闭 |
| | | */ |
| | | public void stopAll() { |
| | | log.info("========== RFID自动连接服务手动关闭 =========="); |
| | | |
| | | // 停止所有设备管理器 |
| | | for (Map.Entry<Integer, DeviceManager> entry : deviceManagers.entrySet()) { |
| | | try { |
| | | log.info("停止RFID设备[{}]自动连接", entry.getKey()); |
| | | entry.getValue().stop(); |
| | | } catch (Exception e) { |
| | | log.error("停止RFID设备[{}]自动连接异常", entry.getKey(), e); |
| | | } |
| | | } |
| | | |
| | | deviceManagers.clear(); |
| | | log.info("RFID自动连接服务已关闭"); |
| | | } |
| | | |
| | | /** |
| | | * 手动停止指定RFID设备连接(通过API调用) |
| | | */ |
| | | public boolean stopDevice(Integer deviceId) { |
| | | DeviceManager manager = deviceManagers.get(deviceId); |
| | | if (manager == null) { |
| | | log.warn("RFID设备[{}]未启用自动连接或不存在", deviceId); |
| | | return false; |
| | | } |
| | | |
| | | try { |
| | | log.info("手动停止RFID设备[{}]自动连接", deviceId); |
| | | manager.stop(); |
| | | deviceManagers.remove(deviceId); |
| | | return true; |
| | | } catch (Exception e) { |
| | | log.error("停止RFID设备[{}]自动连接异常", deviceId, e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 手动启动指定RFID设备连接(通过API调用) |
| | | */ |
| | | public boolean startDevice(Integer deviceId) { |
| | | // 检查设备是否已经在运行 |
| | | if (deviceManagers.containsKey(deviceId)) { |
| | | DeviceManager manager = deviceManagers.get(deviceId); |
| | | if (manager.running.get()) { |
| | | log.warn("RFID设备[{}]已经在运行", deviceId); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // 查找配置 |
| | | RFIDSlave config = null; |
| | | for (RFIDSlave rfidConfigItem : slaveProperties.getRfid()) { |
| | | if (rfidConfigItem != null && rfidConfigItem.getId().equals(deviceId)) { |
| | | config = rfidConfigItem; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (config == null) { |
| | | log.warn("RFID设备[{}]配置不存在", deviceId); |
| | | return false; |
| | | } |
| | | |
| | | // 验证配置 |
| | | if (config.getIp() == null || config.getPort() == null) { |
| | | log.warn("RFID设备[{}]配置不完整", deviceId); |
| | | return false; |
| | | } |
| | | |
| | | // 创建设备管理器并启动 |
| | | DeviceManager manager = new DeviceManager(config, |
| | | globalReconnectInterval, globalTagScanInterval, globalTagScanTime); |
| | | deviceManagers.put(deviceId, manager); |
| | | manager.start(); |
| | | |
| | | log.info("RFID设备[{}]手动启动成功 - IP: {}, Port: {}", |
| | | deviceId, config.getIp(), config.getPort()); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 获取设备连接状态 |
| | | */ |
| | | public Map<String, Object> getDeviceStatus(Integer deviceId) { |
| | | Map<String, Object> result = new java.util.HashMap<>(); |
| | | |
| | | DeviceManager manager = deviceManagers.get(deviceId); |
| | | if (manager == null) { |
| | | result.put("exists", false); |
| | | result.put("message", "设备未启用自动连接"); |
| | | return result; |
| | | } |
| | | |
| | | result.put("exists", true); |
| | | result.put("connected", manager.connected.get()); |
| | | result.put("running", manager.running.get()); |
| | | result.put("ip", manager.config.getIp()); |
| | | result.put("port", manager.config.getPort()); |
| | | result.put("templateConnected", manager.template.isConnected()); |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有设备状态 |
| | | */ |
| | | public List<Map<String, Object>> getAllDeviceStatus() { |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | |
| | | for (Map.Entry<Integer, DeviceManager> entry : deviceManagers.entrySet()) { |
| | | Map<String, Object> status = getDeviceStatus(entry.getKey()); |
| | | status.put("deviceId", entry.getKey()); |
| | | result.add(status); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 重新配置指定设备的天线(用于天线插拔后重新配置) |
| | | */ |
| | | public boolean reconfigureAntenna(Integer deviceId) { |
| | | DeviceManager manager = deviceManagers.get(deviceId); |
| | | if (manager == null) { |
| | | log.warn("RFID设备[{}]未启用自动连接或不存在", deviceId); |
| | | return false; |
| | | } |
| | | |
| | | if (!manager.template.isConnected()) { |
| | | log.warn("RFID设备[{}]未连接,无法配置天线", deviceId); |
| | | return false; |
| | | } |
| | | |
| | | log.info("手动重新配置RFID设备[{}]天线", deviceId); |
| | | return manager.template.reconfigureAntenna(); |
| | | } |
| | | |
| | | public List<Map<String, Object>> getDeviceTags(Integer deviceId, String ip, Integer antenna) { |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | |
| | | DeviceManager manager = null; |
| | | if (ip != null && !ip.isEmpty()) { |
| | | for (DeviceManager m : deviceManagers.values()) { |
| | | if (ip.equals(m.config.getIp())) { |
| | | manager = m; |
| | | break; |
| | | } |
| | | } |
| | | } else if (deviceId != null) { |
| | | manager = deviceManagers.get(deviceId); |
| | | } |
| | | |
| | | if (manager == null || !manager.connected.get() || !manager.template.isConnected()) { |
| | | return result; |
| | | } |
| | | |
| | | List<TagInfo> tags = manager.latestTags; |
| | | if (tags == null || tags.isEmpty()) { |
| | | return result; |
| | | } |
| | | |
| | | for (TagInfo tag : tags) { |
| | | if (antenna != null && tag.antenna != antenna) { |
| | | continue; |
| | | } |
| | | Map<String, Object> tagMap = new java.util.HashMap<>(); |
| | | tagMap.put("epc", tag.epc); |
| | | tagMap.put("rssi", tag.rssi); |
| | | tagMap.put("antenna", tag.antenna); |
| | | tagMap.put("timestamp", tag.timestamp); |
| | | tagMap.put("deviceId", manager.config.getId()); |
| | | tagMap.put("deviceIp", manager.config.getIp()); |
| | | result.add(tagMap); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | public List<Map<String, Object>> queryDeviceTags(Integer deviceId, String ip, Integer antenna) { |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | |
| | | DeviceManager manager = null; |
| | | if (ip != null && !ip.isEmpty()) { |
| | | for (DeviceManager m : deviceManagers.values()) { |
| | | if (ip.equals(m.config.getIp())) { |
| | | manager = m; |
| | | break; |
| | | } |
| | | } |
| | | } else if (deviceId != null) { |
| | | manager = deviceManagers.get(deviceId); |
| | | } |
| | | |
| | | if (manager == null || !manager.connected.get() || !manager.template.isConnected()) { |
| | | return result; |
| | | } |
| | | |
| | | try { |
| | | List<TagInfo> tags = manager.template.readTags(4, 0, manager.tagScanTime); |
| | | if (tags != null && !tags.isEmpty()) { |
| | | for (TagInfo tag : tags) { |
| | | if (antenna != null && tag.antenna != antenna) { |
| | | continue; |
| | | } |
| | | Map<String, Object> tagMap = new java.util.HashMap<>(); |
| | | tagMap.put("epc", tag.epc); |
| | | tagMap.put("rssi", tag.rssi); |
| | | tagMap.put("antenna", tag.antenna); |
| | | tagMap.put("timestamp", tag.timestamp); |
| | | tagMap.put("deviceId", manager.config.getId()); |
| | | tagMap.put("deviceIp", manager.config.getIp()); |
| | | result.add(tagMap); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("查询RFID设备[{}]标签异常", deviceId != null ? deviceId : ip, e); |
| | | } |
| | | return result; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.service; |
| | | |
| | | import com.rfid.uhf288.RFIDTemplate; |
| | | import com.rfid.uhf288.RFIDTemplate.TagInfo; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * RFID服务类 |
| | | * 提供RFID设备检测功能 |
| | | * Created on 2026/01/10 |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class RFIDService { |
| | | |
| | | private RFIDTemplate rfidTemplate; |
| | | private String connectionType = ""; // "COM" 或 "NET" |
| | | |
| | | /** |
| | | * 连接RFID设备(网络连接) |
| | | * |
| | | * @param ip IP地址 |
| | | * @param port 端口号 |
| | | * @return 连接结果 |
| | | */ |
| | | public Map<String, Object> connectNetwork(String ip, Integer port) { |
| | | Map<String, Object> result = new HashMap<>(); |
| | | |
| | | try { |
| | | if (rfidTemplate == null) { |
| | | rfidTemplate = new RFIDTemplate(); |
| | | } |
| | | |
| | | if (rfidTemplate.isConnected()) { |
| | | result.put("success", false); |
| | | result.put("message", "设备已连接,请先断开连接"); |
| | | return result; |
| | | } |
| | | |
| | | int portNum = port != null ? port : 6000; |
| | | boolean connected = rfidTemplate.connectNetwork(ip, portNum); |
| | | |
| | | if (connected) { |
| | | connectionType = "NET"; |
| | | result.put("success", true); |
| | | result.put("message", "连接成功"); |
| | | result.put("ip", ip); |
| | | result.put("port", portNum); |
| | | log.info("RFID设备网络连接成功 - IP: {}, Port: {}", ip, portNum); |
| | | } else { |
| | | result.put("success", false); |
| | | result.put("message", "连接失败,请检查IP和端口"); |
| | | } |
| | | } catch (UnsatisfiedLinkError e) { |
| | | log.error("RFID设备DLL方法链接失败", e); |
| | | result.put("success", false); |
| | | result.put("message", "DLL方法链接失败: " + e.getMessage()); |
| | | result.put("errorType", "UnsatisfiedLinkError"); |
| | | result.put("suggestion", "请检查DLL文件是否正确,或尝试重启应用"); |
| | | } catch (Exception e) { |
| | | log.error("RFID设备连接异常", e); |
| | | result.put("success", false); |
| | | result.put("message", "连接异常: " + e.getMessage()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 连接RFID设备(串口连接) |
| | | * |
| | | * @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 连接结果 |
| | | */ |
| | | public Map<String, Object> connectComPort(Integer port, Integer baud) { |
| | | Map<String, Object> result = new HashMap<>(); |
| | | |
| | | try { |
| | | if (rfidTemplate == null) { |
| | | rfidTemplate = new RFIDTemplate(); |
| | | } |
| | | |
| | | if (rfidTemplate.isConnected()) { |
| | | result.put("success", false); |
| | | result.put("message", "设备已连接,请先断开连接"); |
| | | return result; |
| | | } |
| | | |
| | | int portNum = port != null ? port : 3; // 默认COM3 |
| | | boolean connected; |
| | | |
| | | if (baud != null && baud >= 0 && baud <= 7) { |
| | | connected = rfidTemplate.connectComPort(portNum, baud.byteValue()); |
| | | } else { |
| | | // 自动检测波特率 |
| | | connected = rfidTemplate.connectComPortAuto(portNum, -1); |
| | | } |
| | | |
| | | if (connected) { |
| | | connectionType = "COM"; |
| | | result.put("success", true); |
| | | result.put("message", "连接成功"); |
| | | result.put("port", "COM" + portNum); |
| | | log.info("RFID设备串口连接成功 - COM{}", portNum); |
| | | } else { |
| | | result.put("success", false); |
| | | result.put("message", "连接失败,请检查串口和波特率"); |
| | | } |
| | | } catch (UnsatisfiedLinkError e) { |
| | | log.error("RFID设备DLL方法链接失败", e); |
| | | result.put("success", false); |
| | | result.put("message", "DLL方法链接失败: " + e.getMessage()); |
| | | result.put("errorType", "UnsatisfiedLinkError"); |
| | | result.put("suggestion", "请检查DLL文件是否正确,或尝试重启应用"); |
| | | } catch (Exception e) { |
| | | log.error("RFID设备连接异常", e); |
| | | result.put("success", false); |
| | | result.put("message", "连接异常: " + e.getMessage()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 检测RFID标签(单次检测) |
| | | * 只在检测到标签时打印日志 |
| | | * |
| | | * @param scanTime 扫描时间(单位: 100ms,默认5) |
| | | * @return 检测结果 |
| | | */ |
| | | public Map<String, Object> detectTags(Integer scanTime) { |
| | | Map<String, Object> result = new HashMap<>(); |
| | | List<Map<String, Object>> tagList = new ArrayList<>(); |
| | | |
| | | try { |
| | | if (rfidTemplate == null || !rfidTemplate.isConnected()) { |
| | | result.put("success", false); |
| | | result.put("message", "设备未连接,请先连接设备"); |
| | | return result; |
| | | } |
| | | |
| | | int scanTimeValue = scanTime != null ? scanTime : 5; // 默认5*100ms=500ms |
| | | List<TagInfo> tags = rfidTemplate.readTags(4, 0, scanTimeValue); |
| | | |
| | | if (tags != null && !tags.isEmpty()) { |
| | | // 检测到标签时才打印日志 |
| | | for (TagInfo tag : tags) { |
| | | Map<String, Object> tagMap = new HashMap<>(); |
| | | tagMap.put("epc", tag.epc); |
| | | tagMap.put("rssi", tag.rssi); |
| | | tagMap.put("antenna", tag.antenna); |
| | | tagMap.put("timestamp", tag.timestamp); |
| | | tagList.add(tagMap); |
| | | |
| | | // 打印检测到的标签信息 |
| | | log.info(">>> 检测到RFID标签 - EPC: {}, RSSI: {}dBm, 天线: {}", |
| | | tag.epc, tag.rssi, tag.antenna); |
| | | } |
| | | |
| | | result.put("success", true); |
| | | result.put("message", "检测到 " + tags.size() + " 个标签"); |
| | | result.put("count", tags.size()); |
| | | result.put("tags", tagList); |
| | | } else { |
| | | // 检测不到标签时不打印日志(只返回结果) |
| | | result.put("success", true); |
| | | result.put("message", "未检测到标签"); |
| | | result.put("count", 0); |
| | | result.put("tags", tagList); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("RFID标签检测异常", e); |
| | | result.put("success", false); |
| | | result.put("message", "检测异常: " + e.getMessage()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 断开RFID设备连接 |
| | | * |
| | | * @return 断开结果 |
| | | */ |
| | | public Map<String, Object> disconnect() { |
| | | Map<String, Object> result = new HashMap<>(); |
| | | |
| | | try { |
| | | if (rfidTemplate != null && rfidTemplate.isConnected()) { |
| | | rfidTemplate.disconnect(); |
| | | connectionType = ""; |
| | | result.put("success", true); |
| | | result.put("message", "断开连接成功"); |
| | | log.info("RFID设备连接已断开"); |
| | | } else { |
| | | result.put("success", false); |
| | | result.put("message", "设备未连接"); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("RFID设备断开连接异常", e); |
| | | result.put("success", false); |
| | | result.put("message", "断开连接异常: " + e.getMessage()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 获取连接状态 |
| | | * |
| | | * @return 连接状态 |
| | | */ |
| | | public Map<String, Object> getConnectionStatus() { |
| | | Map<String, Object> result = new HashMap<>(); |
| | | |
| | | try { |
| | | boolean connected = rfidTemplate != null && rfidTemplate.isConnected(); |
| | | result.put("connected", connected); |
| | | result.put("connectionType", connected ? connectionType : ""); |
| | | result.put("message", connected ? "设备已连接" : "设备未连接"); |
| | | } catch (Exception e) { |
| | | log.error("获取RFID连接状态异常", e); |
| | | result.put("connected", false); |
| | | result.put("message", "获取状态异常: " + e.getMessage()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 诊断DLL加载状态 |
| | | * |
| | | * @return 诊断信息 |
| | | */ |
| | | public Map<String, Object> diagnoseDll() { |
| | | Map<String, Object> result = new HashMap<>(); |
| | | |
| | | try { |
| | | // 检查DLL文件是否存在(使用项目根目录的相对路径) |
| | | String projectBaseDir = System.getProperty("user.dir"); |
| | | java.io.File dllFile1 = new java.io.File(projectBaseDir, "src/main/resources/lib/com_rfid_uhf288_Device.dll"); |
| | | java.io.File dllFile2 = new java.io.File(projectBaseDir, "src/main/resources/lib/UHFReader288.dll"); |
| | | |
| | | // 也检查临时目录中的DLL文件 |
| | | String tempDir = System.getProperty("java.io.tmpdir"); |
| | | java.io.File tempDll1 = new java.io.File(tempDir + "rfid-lib/com_rfid_uhf288_Device.dll"); |
| | | java.io.File tempDll2 = new java.io.File(tempDir + "rfid-lib/UHFReader288.dll"); |
| | | |
| | | Map<String, Object> dllInfo = new HashMap<>(); |
| | | Map<String, Object> sourceDll1 = new HashMap<>(); |
| | | sourceDll1.put("exists", dllFile1.exists()); |
| | | sourceDll1.put("size", dllFile1.exists() ? dllFile1.length() : 0); |
| | | sourceDll1.put("path", dllFile1.getAbsolutePath()); |
| | | sourceDll1.put("tempExists", tempDll1.exists()); |
| | | sourceDll1.put("tempSize", tempDll1.exists() ? tempDll1.length() : 0); |
| | | sourceDll1.put("tempPath", tempDll1.getAbsolutePath()); |
| | | if (dllFile1.exists() && tempDll1.exists()) { |
| | | sourceDll1.put("sizeMatch", dllFile1.length() == tempDll1.length()); |
| | | } |
| | | dllInfo.put("com_rfid_uhf288_Device.dll", sourceDll1); |
| | | |
| | | Map<String, Object> sourceDll2 = new HashMap<>(); |
| | | sourceDll2.put("exists", dllFile2.exists()); |
| | | sourceDll2.put("size", dllFile2.exists() ? dllFile2.length() : 0); |
| | | sourceDll2.put("path", dllFile2.getAbsolutePath()); |
| | | sourceDll2.put("tempExists", tempDll2.exists()); |
| | | sourceDll2.put("tempSize", tempDll2.exists() ? tempDll2.length() : 0); |
| | | sourceDll2.put("tempPath", tempDll2.getAbsolutePath()); |
| | | if (dllFile2.exists() && tempDll2.exists()) { |
| | | sourceDll2.put("sizeMatch", dllFile2.length() == tempDll2.length()); |
| | | } |
| | | dllInfo.put("UHFReader288.dll", sourceDll2); |
| | | result.put("dllFiles", dllInfo); |
| | | |
| | | // 检查Device类是否可以实例化 |
| | | boolean deviceCreated = false; |
| | | String deviceError = null; |
| | | try { |
| | | if (rfidTemplate == null) { |
| | | rfidTemplate = new RFIDTemplate(); |
| | | } |
| | | deviceCreated = true; |
| | | } catch (Exception e) { |
| | | deviceError = e.getMessage(); |
| | | } |
| | | |
| | | result.put("deviceCreated", deviceCreated); |
| | | if (deviceError != null) { |
| | | result.put("deviceError", deviceError); |
| | | } |
| | | |
| | | // 检查native方法是否可用(实际尝试调用验证) |
| | | boolean nativeMethodAvailable = false; |
| | | String nativeMethodError = null; |
| | | if (deviceCreated && rfidTemplate != null) { |
| | | try { |
| | | // 实际尝试调用native方法来验证(使用无效参数,只测试方法链接) |
| | | // 如果方法不存在或链接失败,会抛出UnsatisfiedLinkError |
| | | boolean wasConnected = rfidTemplate.isConnected(); |
| | | if (!wasConnected) { |
| | | // 尝试连接一个无效的IP地址,只测试native方法是否可用 |
| | | // 注意:这里会失败,但我们只关心方法是否链接成功 |
| | | try { |
| | | // 调用connectNetwork,如果native方法链接失败会抛出UnsatisfiedLinkError |
| | | boolean connected = rfidTemplate.connectNetwork("127.0.0.1", 9999); |
| | | // 如果执行到这里,说明native方法链接成功(即使连接失败) |
| | | nativeMethodAvailable = true; |
| | | // 如果意外连接成功,断开连接 |
| | | if (connected) { |
| | | rfidTemplate.disconnect(); |
| | | } |
| | | } catch (UnsatisfiedLinkError e) { |
| | | // 如果抛出UnsatisfiedLinkError,说明native方法链接失败 |
| | | nativeMethodError = "DLL native方法链接失败: " + e.getMessage(); |
| | | nativeMethodAvailable = false; |
| | | } catch (Exception e) { |
| | | // 其他异常(如连接失败、网络错误等)是正常的,说明方法链接成功 |
| | | // 检查异常类型,UnsatisfiedLinkError应该已经被上面的catch捕获 |
| | | if (e.getCause() instanceof UnsatisfiedLinkError) { |
| | | nativeMethodError = "DLL native方法链接失败: " + e.getCause().getMessage(); |
| | | nativeMethodAvailable = false; |
| | | } else { |
| | | // 其他异常说明方法链接成功,只是调用失败 |
| | | nativeMethodAvailable = true; |
| | | } |
| | | } |
| | | } else { |
| | | // 如果已经连接,说明之前调用过native方法,方法应该是可用的 |
| | | nativeMethodAvailable = true; |
| | | } |
| | | } catch (UnsatisfiedLinkError e) { |
| | | nativeMethodError = "DLL native方法链接失败: " + e.getMessage(); |
| | | nativeMethodAvailable = false; |
| | | } catch (Exception e) { |
| | | // 其他异常不影响判断 |
| | | if (nativeMethodError == null) { |
| | | nativeMethodError = "验证异常: " + e.getMessage(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | result.put("nativeMethodAvailable", nativeMethodAvailable); |
| | | if (nativeMethodError != null) { |
| | | result.put("nativeMethodError", nativeMethodError); |
| | | } |
| | | |
| | | // 提供建议 |
| | | List<String> suggestions = new ArrayList<>(); |
| | | if (!dllFile1.exists()) { |
| | | suggestions.add("DLL文件不存在: com_rfid_uhf288_Device.dll"); |
| | | suggestions.add("请检查 src/main/resources/lib/ 目录"); |
| | | } else { |
| | | suggestions.add("DLL文件存在: com_rfid_uhf288_Device.dll (大小: " + dllFile1.length() + " 字节)"); |
| | | // 检查DLL文件是否可能是文本文件(被Maven过滤损坏) |
| | | if (dllFile1.length() < 1000) { |
| | | suggestions.add("警告:DLL文件大小异常小,可能被损坏"); |
| | | } |
| | | } |
| | | |
| | | if (!deviceCreated) { |
| | | suggestions.add("Device类实例化失败,请检查DLL文件是否存在"); |
| | | } else if (!nativeMethodAvailable) { |
| | | suggestions.add("DLL已加载,但native方法不可用"); |
| | | suggestions.add("错误信息: " + (nativeMethodError != null ? nativeMethodError : "未知错误")); |
| | | suggestions.add("可能原因:1. DLL版本不匹配 2. 方法签名不匹配 3. DLL文件损坏"); |
| | | suggestions.add("解决方案:"); |
| | | suggestions.add("1. 从demo项目(testdemo-jni)中复制DLL文件到当前项目"); |
| | | suggestions.add("2. 确保DLL文件没有被Maven资源过滤处理"); |
| | | suggestions.add("3. 检查DLL文件大小是否与demo项目中的一致"); |
| | | suggestions.add("4. 尝试重新编译项目: mvn clean compile"); |
| | | suggestions.add("5. 检查临时目录中的DLL文件: " + System.getProperty("java.io.tmpdir") + "rfid-lib"); |
| | | } else { |
| | | suggestions.add("DLL加载正常,native方法可用"); |
| | | } |
| | | |
| | | result.put("suggestions", suggestions); |
| | | result.put("message", nativeMethodAvailable ? "DLL状态正常" : "DLL状态异常"); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("RFID DLL诊断异常", e); |
| | | result.put("error", "诊断异常: " + e.getMessage()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | } |
| | |
| | | port: 5005 |
| | | devpPlcId: ${wcs-slave.devp[1].id} |
| | | staArr: 107 |
| | | crnId: 2 |
| | | crnId: 2 |
| | | |
| | | # RFID读写器配置 |
| | | rfid-config: |
| | | # 全局默认配置 |
| | | reconnectInterval: 10 # 重连间隔(秒) |
| | | tagScanInterval: 500 # 标签检测间隔(毫秒) |
| | | tagScanTime: 10 # 标签扫描时间(单位: 100ms,默认10=1秒) |
| | | # RFID读写器1 |
| | | rfid[0]: |
| | | id: 1 |
| | | ip: 10.10.10.210 |
| | | port: 27011 |
| | | autoConnect: true # 是否自动连接(true: 系统启动时自动连接,false: 手动连接) |
| | | # RFID读写器2 |
| | | rfid[1]: |
| | | id: 2 |
| | | ip: 10.10.10.211 |
| | | port: 27011 |
| | | autoConnect: true # 是否自动连接 |
| | | # RFID读写器3 |
| | | rfid[2]: |
| | | id: 3 |
| | | ip: 10.10.10.212 |
| | | port: 27011 |
| | | autoConnect: true # 是否自动连接 |
| | | # RFID读写器4 |
| | | rfid[3]: |
| | | id: 4 |
| | | ip: 10.10.10.213 |
| | | port: 27011 |
| | | autoConnect: true # 是否自动连接 |
| | |
| | | port: 5005 |
| | | devpPlcId: ${wcs-slave.devp[1].id} |
| | | staArr: 107 |
| | | crnId: 2 |
| | | crnId: 2 |
| | | |
| | | # RFID读写器配置 |
| | | rfid-config: |
| | | # 全局默认配置 |
| | | reconnectInterval: 10 # 重连间隔(秒) |
| | | tagScanInterval: 500 # 标签检测间隔(毫秒) |
| | | tagScanTime: 10 # 标签扫描时间(单位: 100ms,默认10=1秒) |
| | | # RFID读写器1 |
| | | rfid[0]: |
| | | id: 1 |
| | | ip: 10.10.10.210 |
| | | port: 27011 |
| | | autoConnect: true # 是否自动连接(true: 系统启动时自动连接,false: 手动连接) |
| | | # RFID读写器2 |
| | | rfid[1]: |
| | | id: 2 |
| | | ip: 10.10.10.211 |
| | | port: 27011 |
| | | autoConnect: true # 是否自动连接 |
| | | # RFID读写器3 |
| | | rfid[2]: |
| | | id: 3 |
| | | ip: 10.10.10.212 |
| | | port: 27011 |
| | | autoConnect: true # 是否自动连接 |
| | | # RFID读写器4 |
| | | rfid[3]: |
| | | id: 4 |
| | | ip: 10.10.10.213 |
| | | port: 27011 |
| | | autoConnect: true # 是否自动连接 |