| bin/.gitignore | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| bin/chuankou/SerialPortService.class | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| bin/chushihua/SlotManager.class | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| bin/chushihua/lunxun$PollingTask.class | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| bin/chushihua/lunxun.class | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| bin/home/CardMachineUI.class | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| bin/publicway/SerialProtocolParser.class | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| log.properties | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/chuankou/SerialPortService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/chushihua/SlotManager.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/chushihua/lunxun.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/home/CardMachineUI.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/publicway/SerialProtocolParser.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
bin/.gitignore
@@ -1,5 +1,7 @@ /chuankou/ /chushihua/ /dialog/ /home/ /jiekou/ /publicway/ /xitongshezhi/ bin/chuankou/SerialPortService.classBinary files differ
bin/chushihua/SlotManager.classBinary files differ
bin/chushihua/lunxun$PollingTask.classBinary files differ
bin/chushihua/lunxun.classBinary files differ
bin/home/CardMachineUI.classBinary files differ
bin/publicway/SerialProtocolParser.classBinary files differ
log.properties
@@ -1,5 +1,5 @@ #\u64CD\u4F5C\u65E5\u5FD7\u8BB0\u5F55 - \u6700\u540E\u66F4\u65B0: Sat Nov 22 15:43:14 CST 2025 #Sat Nov 22 15:43:14 CST 2025 #\u64CD\u4F5C\u65E5\u5FD7\u8BB0\u5F55 - \u6700\u540E\u66F4\u65B0: Wed Nov 26 13:54:51 CST 2025 #Wed Nov 26 13:54:51 CST 2025 log_1763724957486_7ed8425e=[2025-11-21 19\:35\:57] 取卡操作:卡槽1被管理员取卡 log_1763724957585_8967e0b2=[2025-11-21 19\:35\:57] 取卡操作:卡槽2被管理员取卡 log_1763724957735_6574dba5=[2025-11-21 19\:35\:57] 取卡操作:卡槽3被管理员取卡 @@ -115,3 +115,25 @@ log_1763797394271_ad30a236=[2025-11-22 15\:43\:14] DDCC0008F003515AA55AA5027A7E;type2控制打开3柜门 log_1763797394281_00876aa0=[2025-11-22 15\:43\:14] 取卡操作:卡槽3被管理员取卡 log_1763797394622_9137f63b=[2025-11-22 15\:43\:14] 3号卡槽取卡失败 log_1764135397723_84402e54=[2025-11-26 13\:36\:37] DDCC0008F010515AA55AA502BB5C;type2控制打开16柜门 log_1764135398043_85ab4e55=[2025-11-26 13\:36\:38] 取卡操作:卡槽16被管理员取卡 log_1764135398198_2349ce0d=[2025-11-26 13\:36\:38] DDCC0008F011515AA55AA5027B4C;type2控制打开17柜门 log_1764135398521_707e724d=[2025-11-26 13\:36\:38] 取卡操作:卡槽17被管理员取卡 log_1764135398672_2ff90b31=[2025-11-26 13\:36\:38] DDCC0008F012515AA55AA5027B7F;type2控制打开18柜门 log_1764135399002_72e36700=[2025-11-26 13\:36\:39] 取卡操作:卡槽18被管理员取卡 log_1764135399101_6cfd16ca=[2025-11-26 13\:36\:39] DDCC0008F013515AA55AA502BB6F;type2控制打开19柜门 log_1764135399422_6d2b3023=[2025-11-26 13\:36\:39] 19号卡槽取卡失败 log_1764135400746_19bde6e5=[2025-11-26 13\:36\:40] DDCC0008F012515AA55AA5027B7F;type2控制打开18柜门 log_1764135401072_bfc61929=[2025-11-26 13\:36\:41] 取卡操作:卡槽18被管理员取卡 log_1764135401428_8765a316=[2025-11-26 13\:36\:41] DDCC0008F013515AA55AA502BB6F;type2控制打开19柜门 log_1764135401751_b0dc6eeb=[2025-11-26 13\:36\:41] 取卡操作:卡槽19被管理员取卡 log_1764135403137_780179c3=[2025-11-26 13\:36\:43] DDCC0008F013515AA55AA502BB6F;type2控制打开19柜门 log_1764135403461_84264232=[2025-11-26 13\:36\:43] 取卡操作:卡槽19被管理员取卡 log_1764136489886_1b915fb2=[2025-11-26 13\:54\:49] DDCC0008F010515AA55AA502BB5C;type2控制打开16柜门 log_1764136490213_558da777=[2025-11-26 13\:54\:50] 取卡操作:卡槽16被管理员取卡 log_1764136490338_35642c9c=[2025-11-26 13\:54\:50] DDCC0008F011515AA55AA5027B4C;type2控制打开17柜门 log_1764136490673_e331bdd6=[2025-11-26 13\:54\:50] 取卡操作:卡槽17被管理员取卡 log_1764136490851_72a1541e=[2025-11-26 13\:54\:50] DDCC0008F012515AA55AA5027B7F;type2控制打开18柜门 log_1764136491173_e8aa6057=[2025-11-26 13\:54\:51] 取卡操作:卡槽18被管理员取卡 log_1764136491324_8d1b32d1=[2025-11-26 13\:54\:51] DDCC0008F013515AA55AA502BB6F;type2控制打开19柜门 log_1764136491652_fa44283b=[2025-11-26 13\:54\:51] 取卡操作:卡槽19被管理员取卡 src/chuankou/SerialPortService.java
@@ -6,6 +6,7 @@ import java.io.InputStream; import java.io.OutputStream; import publicway.SerialProtocolParser; // 添加导入 import publicway.TimestampUtil; import xitongshezhi.SystemDebugDialog; public class SerialPortService { @@ -181,6 +182,7 @@ if (len > 0) { buffer.write(readBuffer, 0, len); lastReceivedTime = currentTime; System.out.println("收到原始数据: " + bytesToHex(readBuffer, len)+"时间"+TimestampUtil.getTimestamp()); } if (len <= 0 && buffer.size() == 0) { @@ -264,5 +266,11 @@ int result = port.writeBytes(data, data.length); return result > 0; } private String bytesToHex(byte[] bytes, int length) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < length; i++) { sb.append(String.format("%02X ", bytes[i])); } return sb.toString().trim(); } } src/chushihua/SlotManager.java
@@ -7,6 +7,7 @@ import dialog.Dingshidialog; import home.Fkj; import publicway.TimestampUtil; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -529,7 +530,8 @@ slotNumber+"号卡槽还卡成功感谢您的使用" ); }); //System.out.println("卡槽 " + slotNumber + " 还卡成功,卡号从 " + oldCardNumber + " 变为 " + newCardNumber); // System.out.println("还卡成功" + slotNumber + " 还卡成功,卡号从 " + oldCardNumber + " 变为 " + newCardNumber); System.out.println("还卡成功"+slotNumber +"时间"+ " 还卡成功,卡号从 " + oldCardNumber + " 变为 " + newCardNumber+TimestampUtil.getTimestamp()); } } src/chushihua/lunxun.java
@@ -5,6 +5,7 @@ import home.Fkj; import home.MachineConfig; import publicway.QueryData; import publicway.TimestampUtil; import xitongshezhi.SystemDebugDialog; import java.util.Iterator; @@ -24,7 +25,7 @@ private static volatile boolean isPaused = false; private static final AtomicBoolean shouldStop = new AtomicBoolean(false); private static Thread pollingThread; private static int pollingInterval = 100; // 默认轮询间隔 private static int pollingInterval =50; // 默认轮询间隔 public static boolean sendChaxunzhiling=true;//是否向串口发送查询指令 // 添加静态变量控制人脸检测 public static boolean ishaveface = false; // 是否检测到人脸,默认没有人脸 @@ -381,8 +382,6 @@ while (isRunning && !Thread.currentThread().isInterrupted() && !shouldStop.get()) { try { // System.out.println("查询中.....线程"); // 检查是否暂停 if (isPaused) { synchronized (lunxun.class) { @@ -413,44 +412,37 @@ continue; } // 新增:根据卡槽状态和查询频率决定是否发送查询 boolean sentQuery = false; long currentTime = System.currentTimeMillis(); // 遍历所有卡槽 for (int i = 0; i < slotArray.length; i++) { if (!isRunning || shouldStop.get()) { break; } // 遍历卡槽,寻找需要查询的卡槽 for (int i = 0; i < slotArray.length && !sentQuery; i++) { int slotIndex = (currentIndex + i) % slotArray.length; Fkj slot = slotArray[slotIndex]; if (slot != null) { String hasCard = slot.getHasCard(); int slotNumber = slotIndex + 1; Long lastQueryTime = lastQueryTimeMap.get(slotNumber); // 新增:如果有人脸检测,则只查询无卡卡槽 // 检查是否因为人脸检测需要跳过有卡卡槽 if (ishaveface && "1".equals(hasCard)) { // 跳过有卡卡槽的查询 // 跳过有卡卡槽,立即继续下一个,不等待 if (DEBUG_ENABLED) { debugBuilder.setLength(0); debugBuilder.append("检测到人脸,跳过有卡卡槽 ").append(slotNumber).append(" 的查询\n"); SystemDebugDialog.appendAsciiData(debugBuilder.toString()); } continue; continue; // 立即继续下一个卡槽,不等待 } // 确定查询间隔:只有hasCard="1"的卡槽使用10秒间隔,其他情况(包括"-1")都使用100ms间隔 int queryInterval = "1".equals(hasCard) ? HAS_CARD_QUERY_INTERVAL : NO_CARD_QUERY_INTERVAL; // 发送查询指令到当前卡槽 boolean sendSuccess = sendQueryToSlot(slotNumber); // 检查是否达到查询时间 if (lastQueryTime == null || currentTime - lastQueryTime >= queryInterval) { if (sendQueryToSlot(slotNumber)) { if (sendSuccess) { // 更新最后查询时间 lastQueryTimeMap.put(slotNumber, currentTime); sentQuery = true; lastQueryTimeMap.put(slotNumber, System.currentTimeMillis()); consecutiveFailures = 0; // 成功发送查询后,等待100ms再继续下一个查询 Thread.sleep(100); if (DEBUG_ENABLED) { String status; if ("1".equals(hasCard)) { @@ -461,11 +453,9 @@ status = "无卡"; } // 使用重用的 StringBuilder 构建调试信息 debugBuilder.setLength(0); debugBuilder.append("Slot ").append(slotNumber) .append(" (").append(status).append(") 查询成功,间隔: ") .append(queryInterval).append("ms\n"); .append(" (").append(status).append(") 查询成功\n"); SystemDebugDialog.appendAsciiData(debugBuilder.toString()); } } else { @@ -476,18 +466,15 @@ break; } } } // 发送查询指令后等待50ms,然后继续下一个卡槽 Thread.sleep(50); } } // 更新当前索引 currentIndex = (currentIndex + 1) % slotArray.length; // 如果没有发送查询,等待一段时间再继续 if (!sentQuery) { Thread.sleep(pollingInterval); } } catch (InterruptedException e) { //System.out.println("轮询查询线程被中断"); Thread.currentThread().interrupt(); @@ -527,18 +514,22 @@ * 向指定卡槽发送查询指令 - 优化版本 * 使用缓存指令,优化调试输出 */ /** * 向指定卡槽发送查询指令 - 优化版本 * 使用缓存指令,优化调试输出 */ private boolean sendQueryToSlot(int slotNumber) { try { // 使用缓存的查询指令 String queryCommand = getCachedQueryCommand(slotNumber); // System.out.println("指令是:"+queryCommand); System.out.println(slotNumber+"发送了指令是:"+queryCommand+"时间"+TimestampUtil.getTimestamp()); if (DEBUG_ENABLED) { SystemDebugDialog.appendAsciiData("send to "+slotNumber+" queryCommand"); } if (queryCommand != null && !queryCommand.trim().isEmpty()) { // 发送到串口 if(sendChaxunzhiling) { boolean sendResult = Sendmsg.sendMessage(queryCommand); if (sendResult) { src/home/CardMachineUI.java
@@ -481,14 +481,22 @@ final int slotId = i; SlotStatus status = getSlotStatusFromManager(slotId); // 添加调试信息 // 获取卡槽信息 Fkj slotInfo = slotManager.getSlotInfo(slotId); if (slotInfo != null) { } else { System.err.println("卡槽 " + slotId + " - 获取信息失败"); String cardNumber = "无卡"; if (slotInfo != null && slotInfo.getCardNumber() != null) { String cardNum = slotInfo.getCardNumber(); // 判断卡号是否有效(不是"-1"或"0000") if (!"-1".equals(cardNum) && !"0000".equals(cardNum)) { cardNumber = cardNum; } } JButton slotButton = new JButton(String.valueOf(slotId)); // 创建包含卡槽编号和卡号的HTML文本 String buttonText = String.format("<html><div style='text-align: center;'>%d<br/><span style='font-size: 10px;'>%s</span></div></html>", slotId, cardNumber); JButton slotButton = new JButton(buttonText); slotButton.setBackground(status.getColor()); slotButton.setForeground(Color.WHITE); slotButton.setFont(new Font("Microsoft YaHei", Font.BOLD, 14)); src/publicway/SerialProtocolParser.java
@@ -2,9 +2,12 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.Collections; import java.util.Set; import chuankou.SerialPortService; import chushihua.SlotManager; @@ -54,6 +57,13 @@ // 对象池,减少对象创建 private final Object packetPoolLock = new Object(); // ==================== 新增:数据包去重缓存 ==================== private final Set<String> processedPacketCache = Collections.newSetFromMap( new ConcurrentHashMap<String, Boolean>()); private static final int MAX_CACHE_SIZE = 1000; private static final long CACHE_CLEANUP_INTERVAL = 60000; // 1分钟清理一次缓存 private long lastCacheCleanupTime = 0; /** * 启动解析器 */ @@ -64,9 +74,8 @@ isRunning = true; // 启动批量处理(每50ms处理一次) batchExecutor.scheduleAtFixedRate(this::batchProcess, 50, 50, TimeUnit.MILLISECONDS); // 启动批量处理(每10ms处理一次) batchExecutor.scheduleAtFixedRate(this::batchProcess, 10, 10, TimeUnit.MILLISECONDS); processorThread = new Thread(this::processPackets, "Serial-Protocol-Parser"); processorThread.setDaemon(true); processorThread.start(); @@ -106,6 +115,9 @@ // 清空队列和缓冲区 clearQueues(); bufferPosition = 0; // 清空去重缓存 processedPacketCache.clear(); } /** @@ -203,6 +215,9 @@ // 定期检查内存 checkMemory(); // 定期清理去重缓存 cleanupPacketCache(); } catch (Exception e) { System.err.println("批量处理数据时发生异常: " + e.getMessage()); // 发生异常时重置状态 @@ -275,6 +290,26 @@ } /** * 清理数据包去重缓存 */ private void cleanupPacketCache() { long currentTime = System.currentTimeMillis(); if (currentTime - lastCacheCleanupTime < CACHE_CLEANUP_INTERVAL) { return; } lastCacheCleanupTime = currentTime; // 如果缓存大小超过限制,清空缓存 if (processedPacketCache.size() >= MAX_CACHE_SIZE) { processedPacketCache.clear(); if (lunxun.DEBUG_ENABLED) { SystemDebugDialog.appendAsciiData("数据包去重缓存已清空,当前大小: " + processedPacketCache.size()); } } } /** * 处理缓冲区中的数据,解析完整数据包 */ private void processBuffer() { @@ -318,6 +353,27 @@ // 提取完整数据包 byte[] packet = extractPacket(startIndex, totalPacketLength); if (packet != null) { // ==================== 新增:数据包去重检查 ==================== String packetHash = generatePacketHash(packet); if (processedPacketCache.contains(packetHash)) { // 跳过重复数据包 if (lunxun.DEBUG_ENABLED) { SystemDebugDialog.appendAsciiData("跳过重复数据包: " + bytesToHex(packet)); } // 移动缓冲区位置,继续处理下一个包 int remaining = bufferPosition - (startIndex + totalPacketLength); if (remaining > 0) { System.arraycopy(dataBuffer, startIndex + totalPacketLength, dataBuffer, 0, remaining); } bufferPosition = remaining; processedCount++; continue; } // 添加到去重缓存 processedPacketCache.add(packetHash); // 将数据包放入队列供解析 if (!dataQueue.offer(packet)) { // 队列已满,释放packet引用 @@ -339,6 +395,35 @@ } /** * 生成数据包哈希值用于去重 * 基于关键字段:主机地址 + 卡槽地址 + 功能码 + 数据内容 */ private String generatePacketHash(byte[] packet) { if (packet == null || packet.length < 7) { return ""; } StringBuilder key = new StringBuilder(64); // 添加协议头信息 key.append(packet[4] & 0xFF).append(":"); // 主机地址 (位置4) key.append(packet[5] & 0xFF).append(":"); // 卡槽地址 (位置5) key.append(packet[6] & 0xFF).append(":"); // 功能码 (位置6) // 添加数据内容哈希(排除包头和CRC) int dataStart = 7; // 数据开始位置 int dataEnd = packet.length - 2; // 排除CRC字节 // 取数据内容的前20个字节作为哈希依据 int hashLength = Math.min(dataEnd - dataStart, 20); for (int i = dataStart; i < dataStart + hashLength && i < dataEnd; i++) { key.append(String.format("%02X", packet[i])); } return key.toString(); } /** * 提取数据包,重用byte数组减少对象创建 */ private byte[] extractPacket(int startIndex, int totalPacketLength) { @@ -408,6 +493,7 @@ * 解析数据包并根据功能码调用相应方法 */ private void parsePacket(byte[] packet) { System.out.println("开始解析收到的数据"+bytesToHex(packet)+"时间"+TimestampUtil.getTimestamp()); if (packet == null || packet.length < MIN_PACKET_LENGTH) { return; } @@ -418,6 +504,7 @@ byte hostAddress = packet[4]; // 主机地址 byte slotAddress = packet[5]; // 卡槽地址 byte functionCode = packet[6]; // 功能码 // 数据长度 (从协议中读取) int dataLength = ((packet[2] & 0xFF) << 8) | (packet[3] & 0xFF); @@ -529,14 +616,16 @@ * 获取解析器状态信息 */ public String getStatusInfo() { return String.format("串口解析器状态: %s, 队列大小: %d/%d, 批量队列: %d/%d, 缓冲区: %d/%d", return String.format("串口解析器状态: %s, 队列大小: %d/%d, 批量队列: %d/%d, 缓冲区: %d/%d, 去重缓存: %d/%d", isRunning ? "运行中" : "已停止", dataQueue.size(), dataQueue.remainingCapacity() + dataQueue.size(), batchQueue.size(), batchQueue.remainingCapacity() + batchQueue.size(), bufferPosition, dataBuffer.length); dataBuffer.length, processedPacketCache.size(), MAX_CACHE_SIZE); } /** @@ -554,5 +643,23 @@ clearQueues(); bufferPosition = 0; hexBuilder.setLength(0); processedPacketCache.clear(); } /** * 手动清空数据包去重缓存(用于调试或特殊情况) */ public void clearPacketCache() { processedPacketCache.clear(); if (lunxun.DEBUG_ENABLED) { SystemDebugDialog.appendAsciiData("数据包去重缓存已手动清空"); } } /** * 获取去重缓存大小(用于监控) */ public int getPacketCacheSize() { return processedPacketCache.size(); } }