张世豪
2025-11-26 2b756769ea4adad21332d8a294871712cd42cc3f
优化了轮询逻辑注意有打印输出
已修改13个文件
234 ■■■■ 文件已修改
bin/.gitignore 2 ●●●●● 补丁 | 查看 | 原始文档 | 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 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/chuankou/SerialPortService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/chushihua/SlotManager.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/chushihua/lunxun.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/home/CardMachineUI.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/publicway/SerialProtocolParser.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/.gitignore
@@ -1,5 +1,7 @@
/chuankou/
/chushihua/
/dialog/
/home/
/jiekou/
/publicway/
/xitongshezhi/
bin/chuankou/SerialPortService.class
Binary files differ
bin/chushihua/SlotManager.class
Binary files differ
bin/chushihua/lunxun$PollingTask.class
Binary files differ
bin/chushihua/lunxun.class
Binary files differ
bin/home/CardMachineUI.class
Binary files differ
bin/publicway/SerialProtocolParser.class
Binary 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();
    }
}