03b0fb0ba2de86bcfff277778826547c0e37a93f..d22349714c8d199c02f336f90fba841ef8f5cd39
2 小时以前 张世豪
优化内存后最终版202511211746
d22349 对比 | 目录
3 小时以前 张世豪
可发布版本202517.25
100f4d 对比 | 目录
4 小时以前 张世豪
修改
a60772 对比 | 目录
已添加1个文件
已删除4个文件
已重命名3个文件
已修改62个文件
已复制1个文件
4405 ■■■■ 文件已修改
.settings/org.eclipse.core.resources.prefs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/.gitignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/chuankou/Sendmsg.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/chuankou/SerialDataReceiver.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/chuankou/SerialPortService.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/chushihua/Chushihua.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/chushihua/SlotManager.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/chushihua/lunxun$PollingTask.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/chushihua/lunxun.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/home/CardMachineUI$1.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/home/CardMachineUI$SlotStatus.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/home/CardMachineUI.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/home/Homein.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/publicway/ProtocolParser01$CardStatus.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/publicway/ProtocolParser01$DoorStatus.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/publicway/ProtocolParser01$FaultType.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/publicway/ProtocolParser01$ParseResult.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/publicway/ProtocolParser01$ParseResultPool.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/publicway/ProtocolParser01$WorkStatus.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/publicway/ProtocolParser01.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/publicway/SerialProtocolParser.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/ConfigSet$1.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/ConfigSet$2.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/ConfigSet$3.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/ConfigSet$MenuItemListener.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/ConfigSet.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/Fkj.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kacaoguanli$1.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kacaoguanli$2.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kacaoguanli$3.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kacaoguanli$4.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kacaoguanli$5.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kacaoguanli$CustomTableCellRenderer.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kacaoguanli$StatusInfo.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kacaoguanli.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kuaisuquka$1.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kuaisuquka$2.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kuaisuquka$3.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kuaisuquka$4.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kuaisuquka$5.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kuaisuquka$6.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kuaisuquka$SlotButtonListener.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kuaisuquka$SlotStatus.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/kuaisuquka.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/lishijilu$1.class 补丁 | 查看 | 原始文档 | blame | 历史
bin/xitongshezhi/lishijilu.class 补丁 | 查看 | 原始文档 | blame | 历史
config.properties 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
err.properties 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
log.properties 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/chuankou/Sendmsg.java 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/chuankou/SerialDataReceiver.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/chuankou/SerialPortService.java 118 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/chushihua/Chushihua.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/chushihua/SlotManager.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/chushihua/lunxun.java 1508 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/chushihua/lunxunzaixian.java 506 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dialog/Charulog.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dialog/Dingshidialog.java 45 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dialog/Errlog.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/home/CardMachineUI.java 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/home/Fkj.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/home/Homein.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/jiekou/lunxunkazhuangtai.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/publicway/OpenDoor.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/publicway/ProtocolParser01.java 371 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/publicway/SerialProtocolParser.java 288 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/xitongshezhi/ConfigSet.java 77 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/xitongshezhi/SystemDebugDialog.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/xitongshezhi/kacaoguanli.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/xitongshezhi/kuaisuquka.java 553 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/xitongshezhi/lishijilu.java 306 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.settings/org.eclipse.core.resources.prefs
@@ -3,4 +3,5 @@
encoding//src/publicway/ProtocolParser01.java=GBK
encoding//src/publicway/ProtocolParser51.java=GBK
encoding//src/publicway/SerialProtocolParser.java=UTF-8
encoding/log.properties=GBK
encoding/err.properties=utf8
encoding/log.properties=UTF8
bin/.gitignore
@@ -4,3 +4,4 @@
/xitongshezhi/
/chuankou/
/publicway/
/dialog/
bin/chuankou/Sendmsg.class
Binary files differ
bin/chuankou/SerialDataReceiver.class
Binary files differ
bin/chuankou/SerialPortService.class
Binary files differ
bin/chushihua/Chushihua.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$1.class
Binary files differ
bin/home/CardMachineUI$SlotStatus.class
Binary files differ
bin/home/CardMachineUI.class
Binary files differ
bin/home/Homein.class
Binary files differ
bin/publicway/ProtocolParser01$CardStatus.class
Binary files differ
bin/publicway/ProtocolParser01$DoorStatus.class
Binary files differ
bin/publicway/ProtocolParser01$FaultType.class
Binary files differ
bin/publicway/ProtocolParser01$ParseResult.class
Binary files differ
bin/publicway/ProtocolParser01$ParseResultPool.class
Binary files differ
bin/publicway/ProtocolParser01$WorkStatus.class
Binary files differ
bin/publicway/ProtocolParser01.class
Binary files differ
bin/publicway/SerialProtocolParser.class
Binary files differ
bin/xitongshezhi/ConfigSet$1.class
Binary files differ
bin/xitongshezhi/ConfigSet$2.class
Binary files differ
bin/xitongshezhi/ConfigSet$3.class
Binary files differ
bin/xitongshezhi/ConfigSet$MenuItemListener.class
Binary files differ
bin/xitongshezhi/ConfigSet.class
Binary files differ
bin/xitongshezhi/Fkj.class
Binary files differ
bin/xitongshezhi/kacaoguanli$1.class
Binary files differ
bin/xitongshezhi/kacaoguanli$2.class
Binary files differ
bin/xitongshezhi/kacaoguanli$3.class
Binary files differ
bin/xitongshezhi/kacaoguanli$4.class
Binary files differ
bin/xitongshezhi/kacaoguanli$5.class
Binary files differ
bin/xitongshezhi/kacaoguanli$CustomTableCellRenderer.class
Binary files differ
bin/xitongshezhi/kacaoguanli$StatusInfo.class
Binary files differ
bin/xitongshezhi/kacaoguanli.class
Binary files differ
bin/xitongshezhi/kuaisuquka$1.class
Binary files differ
bin/xitongshezhi/kuaisuquka$2.class
Binary files differ
bin/xitongshezhi/kuaisuquka$3.class
Binary files differ
bin/xitongshezhi/kuaisuquka$4.class
Binary files differ
bin/xitongshezhi/kuaisuquka$5.class
Binary files differ
bin/xitongshezhi/kuaisuquka$6.class
Binary files differ
bin/xitongshezhi/kuaisuquka$SlotButtonListener.class
Binary files differ
bin/xitongshezhi/kuaisuquka$SlotStatus.class
Binary files differ
bin/xitongshezhi/kuaisuquka.class
Binary files differ
bin/xitongshezhi/lishijilu$1.class
Binary files differ
bin/xitongshezhi/lishijilu.class
Binary files differ
config.properties
@@ -10,3 +10,6 @@
server.port=8081
system.language=zh-CN
total.slots=60
popup.display.time=5
error.log.server.address=39.106.210.13
error.log.server.port=8082
err.properties
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
#\u64CD\u4F5C\u65E5\u5FD7\u8BB0\u5F55 - \u6700\u540E\u66F4\u65B0: Fri Nov 21 15:53:26 CST 2025
#Fri Nov 21 15:53:26 CST 2025
log_1763709005376_be67189d=[2025-11-21 15\:10\:05] å–卡操作:12132232
log_1763709010792_533f9544=[2025-11-21 15\:10\:10] å–卡操作:卡槽14被管理员取卡
log_1763711606778_258ce9b3=[2025-11-21 15\:53\:26] [15\:53\:26.776] å‘送失败,正在重试 (1/2)
log_1763711606820_621ce6a5=[2025-11-21 15\:53\:26] [15\:53\:26.820] å‘送失败,正在重试 (1/2)
log_1763711606821_a4bb65ce=[2025-11-21 15\:53\:26] [15\:53\:26.821] å‘送失败,正在重试 (1/2)
log_1763711606822_e9cc5b23=[2025-11-21 15\:53\:26] [15\:53\:26.822] å‘送失败,正在重试 (1/2)
log_1763711606823_1da1f119=[2025-11-21 15\:53\:26] [15\:53\:26.823] å‘送失败,正在重试 (1/2)
log_1763711606824_15788a29=[2025-11-21 15\:53\:26] lunxun连续失败次数过多,暂停轮询
log.properties
@@ -1,5 +1,33 @@
#\u64CD\u4F5C\u65E5\u5FD7\u8BB0\u5F55 - \u6700\u540E\u66F4\u65B0: Thu Nov 20 20:49:18 CST 2025
#Thu Nov 20 20:49:18 CST 2025
log_1763642944396_d82061c8=[2025-11-20 20\:49\:04] \u53D6\u5361\u64CD\u4F5C\uFF1A\u5361\u69FD14\u88AB\u7BA1\u7406\u5458\u53D6\u5361
log_1763642958908_d200dc13=[2025-11-20 20\:49\:18] \u53D6\u5361\u64CD\u4F5C\uFF1A\u5361\u69FD25\u88AB\u7BA1\u7406\u5458\u53D6\u5361
\u00B2\u00E2\u00CA\u00D4=
#\u64CD\u4F5C\u65E5\u5FD7\u8BB0\u5F55 - \u6700\u540E\u66F4\u65B0: Fri Nov 21 17:44:41 CST 2025
#Fri Nov 21 17:44:41 CST 2025
log_1763718093181_61c0dfbe=[2025-11-21 17\:41\:33] å–卡操作:卡槽13被管理员取卡
log_1763718093533_c3563f25=[2025-11-21 17\:41\:33] å–卡操作:卡槽13被管理员取卡
log_1763718147310_e84a6b5b=[2025-11-21 17\:42\:27] å–卡操作:卡槽22被管理员取卡
log_1763718150490_e2f94107=[2025-11-21 17\:42\:30] å–卡操作:卡槽23被管理员取卡
log_1763718150843_c9147690=[2025-11-21 17\:42\:30] å–卡操作:卡槽23被管理员取卡
log_1763718198841_27d9aa77=[2025-11-21 17\:43\:18] å–卡操作:卡槽12被管理员取卡
log_1763718199155_4cd5e39f=[2025-11-21 17\:43\:19] å–卡操作:卡槽12被管理员取卡
log_1763718207706_07760e2f=[2025-11-21 17\:43\:27] DDCC0008F003515AA55AA5027A7E;type2控制打开3柜门
log_1763718208053_ee80022a=[2025-11-21 17\:43\:28] 3号卡槽取卡失败
log_1763718208759_e8be3ec0=[2025-11-21 17\:43\:28] DDCC0008F003515AA55AA5027A7E;type2控制打开3柜门
log_1763718209104_247397ce=[2025-11-21 17\:43\:29] å–卡操作:卡槽3被管理员取卡
log_1763718275945_e2a4d769=[2025-11-21 17\:44\:35] DDCC0008F012515AA55AA5027B7F;type2控制打开18柜门
log_1763718276293_034826af=[2025-11-21 17\:44\:36] å–卡操作:卡槽18被管理员取卡
log_1763718276666_18777eee=[2025-11-21 17\:44\:36] DDCC0008F017515AA55AA5027B2A;type2控制打开23柜门
log_1763718276992_c9ad983a=[2025-11-21 17\:44\:36] å–卡操作:卡槽23被管理员取卡
log_1763718277268_15912a52=[2025-11-21 17\:44\:37] DDCC0008F01C515AA55AA502BB90;type2控制打开28柜门
log_1763718277593_d9b897ba=[2025-11-21 17\:44\:37] å–卡操作:卡槽28被管理员取卡
log_1763718277892_72d570bd=[2025-11-21 17\:44\:37] DDCC0008F018515AA55AA5027BD5;type2控制打开24柜门
log_1763718278242_75327d86=[2025-11-21 17\:44\:38] å–卡操作:卡槽24被管理员取卡
log_1763718278782_a91dddda=[2025-11-21 17\:44\:38] DDCC0008F00E515AA55AA502BAA2;type2控制打开14柜门
log_1763718279143_4e4cb89a=[2025-11-21 17\:44\:39] å–卡操作:卡槽14被管理员取卡
log_1763718279202_9c58a3b9=[2025-11-21 17\:44\:39] DDCC0008F013515AA55AA502BB6F;type2控制打开19柜门
log_1763718279543_9e40ceff=[2025-11-21 17\:44\:39] å–卡操作:卡槽19被管理员取卡
log_1763718279709_4483c6c4=[2025-11-21 17\:44\:39] DDCC0008F01D515AA55AA5027B80;type2控制打开29柜门
log_1763718280043_06750db2=[2025-11-21 17\:44\:40] å–卡操作:卡槽29被管理员取卡
log_1763718280658_b6288ce6=[2025-11-21 17\:44\:40] DDCC0008F00F515AA55AA5027AB2;type2控制打开15柜门
log_1763718280993_f6f218cc=[2025-11-21 17\:44\:40] å–卡操作:卡槽15被管理员取卡
log_1763718281030_2d1edd96=[2025-11-21 17\:44\:41] DDCC0008F014515AA55AA5027B19;type2控制打开20柜门
log_1763718281392_c8b2ee8c=[2025-11-21 17\:44\:41] å–卡操作:卡槽20被管理员取卡
log_1763718281404_56d7aa71=[2025-11-21 17\:44\:41] DDCC0008F019515AA55AA502BBC5;type2控制打开25柜门
log_1763718281743_12a79e45=[2025-11-21 17\:44\:41] å–卡操作:卡槽25被管理员取卡
src/chuankou/Sendmsg.java
@@ -1,21 +1,156 @@
package chuankou;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.SwingWorker;
import chushihua.lunxun;
import dialog.Charulog;
import dialog.Dingshidialog;
import dialog.Errlog;
import publicway.OpenDoor;
import xitongshezhi.SystemDebugDialog;
/**
 * ä¸²å£æ¶ˆæ¯å‘送工具类
 * æä¾›é«˜æ€§èƒ½çš„串口消息发送功能,适合高频调用
 * ä¼˜åŒ–内存使用,避免长时间运行内存泄漏
 */
public class Sendmsg {
    // é™æ€ä¸²å£æœåŠ¡å®žä¾‹
    private static volatile SerialPortService serialService = null;
    private static volatile boolean isPortOpen = false;
    private static final AtomicBoolean isPortOpen = new AtomicBoolean(false);
    
    // æ”¹ä¸ºéžfinal,支持动态控制
    private static boolean DEBUG_MODE = false;
    private static volatile boolean DEBUG_MODE = false;
    
    // æ—¥æœŸæ ¼å¼åŒ–
    private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS");
    // ä½¿ç”¨ThreadLocal保证SimpleDateFormat线程安全
    private static final ThreadLocal<SimpleDateFormat> TIME_FORMATTER =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("HH:mm:ss.SSS"));
    // ç¼“存字符串构建器,减少对象创建
    private static final ThreadLocal<StringBuilder> STRING_BUILDER_CACHE =
        ThreadLocal.withInitial(() -> new StringBuilder(128));
    // è®°å½•活跃的SwingWorker,便于管理
    private static final ConcurrentHashMap<String, SwingWorker<?, ?>> ACTIVE_WORKERS =
        new ConcurrentHashMap<>();
    /**发卡服务器控制打开某个柜门调用指令
     * @param int slotId柜门编号1-60
     * @param int type 1是服务器发卡,2是管理员发卡*/
    public static boolean opendoorzhiling(int slotId,int type) {
        lunxun.setSendChaxunzhiling(false);//暂停查询指令
        try {
            // è°ƒç”¨OpenDoor生成开门指令
            String command = OpenDoor.openOneDoor(slotId, type);
            boolean sendResult = Sendmsg.sendMessage(command);
            StringBuilder mesBuilder = STRING_BUILDER_CACHE.get();
            mesBuilder.setLength(0); // æ¸…空重用
            mesBuilder.append(command).append(";type").append(type).append("控制打开").append(slotId).append("柜门");
            String mes = mesBuilder.toString();
            Charulog.logOperation(mes);
            if (lunxun.DEBUG_ENABLED) {
                SystemDebugDialog.appendAsciiData(mes);
            }
            return sendResult;
        } finally {
            // ç¡®ä¿æ¢å¤æŸ¥è¯¢æŒ‡ä»¤
            lunxun.setSendChaxunzhiling(true);
        }
    }
    /**
     * æ‰“开全部卡槽的公用静态方法
     * @param type æ“ä½œç±»åž‹ï¼š1-服务器发卡,2-管理员发卡
     */
    public static void openAllSlots(int type) {
        lunxun.setSendChaxunzhiling(false);//暂停查询指令
        String workerKey = "openAllSlots_" + System.currentTimeMillis();
        // ä½¿ç”¨SwingWorker在后台执行,避免阻塞UI
        SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
            @Override
            protected Void doInBackground() throws Exception {
                // éåŽ†æ‰€æœ‰å¡æ§½ï¼ˆ1-60)
                for (int slotId = 1; slotId <= 60 && !isCancelled(); slotId++) {
                    try {
                        // ç”Ÿæˆå¼€é—¨æŒ‡ä»¤
                        String command = OpenDoor.openOneDoor(slotId, type);
                        // å‘送串口指令
                        boolean sent = Sendmsg.sendMessage(command);
                        if (!sent) {
                            Errlog.logOperation("发送指令到卡槽 " + slotId + " å¤±è´¥");
                        }
                        // é—´éš”100ms,但检查是否被取消
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        break; // è¢«ä¸­æ–­æ—¶é€€å‡ºå¾ªçޝ
                    } catch (Exception e) {
                        Errlog.logOperation("处理卡槽 " + slotId + " æ—¶å‘生错误: " + e.getMessage());
                        // ç»§ç»­å¤„理下一个卡槽,不中断循环
                    }
                }
                return null;
            }
            @Override
            protected void done() {
                try {
                    // æ¸…理worker引用
                    ACTIVE_WORKERS.remove(workerKey);
                    // å¯é€‰ï¼šå®ŒæˆåŽå¯ä»¥æ·»åŠ å›žè°ƒå¤„ç†
                    if (!isCancelled()) {
                        StringBuilder messageBuilder = STRING_BUILDER_CACHE.get();
                        messageBuilder.setLength(0);
                        String types = "管理员";
                        if(type == 1) {
                            types = "服务器指令";
                        }
                        messageBuilder.append(types).append("已将全部卡槽已经打开请取卡");
                        String message = messageBuilder.toString();
                        Dingshidialog.showTimedDialog(null, 5, message);
                        Charulog.logOperation(message);
                    }
                } finally {
                    // ç¡®ä¿æ¢å¤æŸ¥è¯¢æŒ‡ä»¤
                    lunxun.setSendChaxunzhiling(true);
                }
            }
        };
        // è®°å½•worker便于管理
        ACTIVE_WORKERS.put(workerKey, worker);
        worker.execute();
    }
    /**
     * å–消所有正在执行的任务
     */
    public static void cancelAllTasks() {
        ACTIVE_WORKERS.forEach((key, worker) -> {
            if (!worker.isDone()) {
                worker.cancel(true);
            }
        });
        ACTIVE_WORKERS.clear();
    }
    
    /**
     * è®¾ç½®ä¸²å£æœåŠ¡å®žä¾‹
@@ -24,7 +159,7 @@
     */
    public static void setSerialService(SerialPortService service, boolean open) {
        serialService = service;
        isPortOpen = open;
        isPortOpen.set(open);
    }
    
    /**
@@ -38,13 +173,13 @@
     * å‘送消息到串口(带重试机制)
     */
    public static boolean sendMessage(String message) {
        if (!isPortOpen || serialService == null) {
            System.err.println("[" + getCurrentTime() + "] ä¸²å£æœªæ‰“开,无法发送数据");
        if (!isPortOpen.get() || serialService == null) {
            Errlog.logOperation("[" + getCurrentTime() + "] ä¸²å£æœªæ‰“开,无法发送数据");
            return false;
        }
        
        if (message == null || message.trim().isEmpty()) {
            System.err.println("[" + getCurrentTime() + "] å‘送数据为空");
            Errlog.logOperation("[" + getCurrentTime() + "] å‘送数据为空");
            return false;
        }
        
@@ -56,7 +191,7 @@
            try {
                byte[] data = hexStringToByteArray(text);
                if (data == null) {
                    System.err.println("[" + getCurrentTime() + "] HEX转换失败,数据: " + text);
                    Errlog.logOperation("[" + getCurrentTime() + "] HEX转换失败,数据: " + text);
                    return false;
                }
                
@@ -70,7 +205,7 @@
                } else {
                    retryCount++;
                    if (retryCount <= MAX_RETRY) {
                        System.err.println("[" + getCurrentTime() + "] å‘送失败,正在重试 (" + retryCount + "/" + MAX_RETRY + ")");
                        Errlog.logOperation("[" + getCurrentTime() + "] å‘送失败,正在重试 (" + retryCount + "/" + MAX_RETRY + ")");
                        try {
                            Thread.sleep(50); // é‡è¯•前等待
                        } catch (InterruptedException e) {
@@ -78,17 +213,21 @@
                            break;
                        }
                    } else {
                        System.err.println("[" + getCurrentTime() + "] ä¸²å£å‘送失败,指令: " + text.toUpperCase());
                        System.err.println("[" + getCurrentTime() + "] ä¸²å£çŠ¶æ€ - æ‰“å¼€: " + isPortOpen + ", æœåŠ¡: " + (serialService != null));
                        if (serialService != null) {
                            System.err.println("[" + getCurrentTime() + "] ä¸²å£æœåŠ¡çŠ¶æ€ - æ˜¯å¦æ‰“å¼€: " + serialService.isOpen());
                        if (DEBUG_MODE) {
                            Errlog.logOperation("[" + getCurrentTime() + "] ä¸²å£å‘送失败,指令: " + text.toUpperCase());
                            Errlog.logOperation("[" + getCurrentTime() + "] ä¸²å£çŠ¶æ€ - æ‰“å¼€: " + isPortOpen.get() + ", æœåŠ¡: " + (serialService != null));
                            if (serialService != null) {
                                Errlog.logOperation("[" + getCurrentTime() + "] ä¸²å£æœåŠ¡çŠ¶æ€ - æ˜¯å¦æ‰“å¼€: " + serialService.isOpen());
                            }
                        }
                        return false;
                    }
                }
            } catch (Exception e) {
                System.err.println("[" + getCurrentTime() + "] å‘送异常,指令: " + text.toUpperCase());
                e.printStackTrace();
                Errlog.logOperation("[" + getCurrentTime() + "] å‘送异常,指令: " + text.toUpperCase());
                if (DEBUG_MODE) {
                    e.printStackTrace();
                }
                return false;
            }
        }
@@ -101,9 +240,9 @@
     * @return ä¸²å£æ‰“开状态
     */
    public static boolean isPortOpen() {
        boolean open = isPortOpen && serialService != null;
        boolean open = isPortOpen.get() && serialService != null;
        if (!open && DEBUG_MODE) {
            System.err.println("[" + getCurrentTime() + "] ä¸²å£çŠ¶æ€æ£€æŸ¥: æœªæ‰“å¼€");
            Errlog.logOperation("[" + getCurrentTime() + "] ä¸²å£çŠ¶æ€æ£€æŸ¥: æœªæ‰“å¼€");
        }
        return open;
    }
@@ -148,7 +287,8 @@
            return "";
        }
        
        StringBuilder sb = new StringBuilder();
        StringBuilder sb = STRING_BUILDER_CACHE.get();
        sb.setLength(0);
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
@@ -160,7 +300,7 @@
     * @return æ—¶é—´å­—符串
     */
    private static String getCurrentTime() {
        return timeFormat.format(new Date());
        return TIME_FORMATTER.get().format(new Date());
    }
    
    /**
@@ -186,4 +326,26 @@
        DEBUG_MODE = debug;
        System.out.println("[" + getCurrentTime() + "] Sendmsg调试模式: " + (debug ? "启用" : "禁用"));
    }
    /**
     * æ¸…理资源,防止内存泄漏
     */
    public static void cleanup() {
        cancelAllTasks();
        // æ¸…理ThreadLocal资源
        TIME_FORMATTER.remove();
        STRING_BUILDER_CACHE.remove();
        if (DEBUG_MODE) {
            System.out.println("[" + getCurrentTime() + "] Sendmsg资源清理完成");
        }
    }
    /**
     * èŽ·å–æ´»è·ƒä»»åŠ¡æ•°é‡
     */
    public static int getActiveTaskCount() {
        return ACTIVE_WORKERS.size();
    }
}
src/chuankou/SerialDataReceiver.java
@@ -6,21 +6,26 @@
public class SerialDataReceiver {
    private static final int BUFFER_SIZE = 1024;
    private static byte[] staticDataBuffer = new byte[BUFFER_SIZE];
    private static int staticBufferPosition = 0;
    private static final int MIN_PACKET_LENGTH = 9;
    private static final byte[] START_MARKER = {(byte) 0xDD, (byte) 0xCC};
    // ä½¿ç”¨éžé™æ€æˆå‘˜é¿å…å¤šçº¿ç¨‹çŽ¯å¢ƒä¸‹çš„ç«žäº‰æ¡ä»¶
    private byte[] dataBuffer = new byte[BUFFER_SIZE];
    private int bufferPosition = 0;
    private final List<byte[]> reusablePackets = new ArrayList<>();
    
    /**
     * é™æ€æ–¹æ³•:接收串口原始数据并解析完整数据包
     * å®žä¾‹æ–¹æ³•:接收串口原始数据并解析完整数据包
     * @param rawData åŽŸå§‹æ•°æ®
     * @param debugEnabled æ˜¯å¦å¯ç”¨è°ƒè¯•
     * @param maxRawDataPrintLength æœ€å¤§æ‰“印长度
     * @return è§£æžå‡ºçš„完整数据包列表,如果没有完整包则返回空列表
     */
    public static List<byte[]> receiveData(byte[] rawData, boolean debugEnabled, int maxRawDataPrintLength) {
        List<byte[]> completePackets = new ArrayList<>();
    public List<byte[]> receiveData(byte[] rawData, boolean debugEnabled, int maxRawDataPrintLength) {
        reusablePackets.clear();
        
        if (rawData == null || rawData.length == 0) {
            return completePackets;
            return reusablePackets;
        }
        
        // æ‰“印原始接收数据(调试用)
@@ -28,88 +33,114 @@
            printRawData("收到串口原始数据", rawData, maxRawDataPrintLength);
        }
        
        // å°†æ•°æ®æ·»åŠ åˆ°ç¼“å†²åŒº
        if (staticBufferPosition + rawData.length > staticDataBuffer.length) {
        // æ£€æŸ¥ç¼“冲区容量,动态处理
        if (!ensureBufferCapacity(rawData.length)) {
            // ç¼“冲区不足时,清理并重新开始
            System.arraycopy(staticDataBuffer, staticBufferPosition - rawData.length,
                           staticDataBuffer, 0, rawData.length);
            staticBufferPosition = rawData.length;
        } else {
            System.arraycopy(rawData, 0, staticDataBuffer, staticBufferPosition, rawData.length);
            staticBufferPosition += rawData.length;
            if (debugEnabled) {
                System.out.println("缓冲区不足,清空缓冲区重新开始");
            }
            bufferPosition = 0;
        }
        
        // å¤„理缓冲区中的数据并收集完整包
        processBuffer(completePackets, debugEnabled);
        // å°†æ•°æ®æ·»åŠ åˆ°ç¼“å†²åŒº
        System.arraycopy(rawData, 0, dataBuffer, bufferPosition, rawData.length);
        bufferPosition += rawData.length;
        
        return completePackets;
        // å¤„理缓冲区中的数据并收集完整包
        processBuffer(reusablePackets, debugEnabled);
        return new ArrayList<>(reusablePackets);
    }
    /**
     * ç¡®ä¿ç¼“冲区有足够容量,如不够则尝试压缩
     */
    private boolean ensureBufferCapacity(int required) {
        if (bufferPosition + required <= dataBuffer.length) {
            return true;
        }
        // å°è¯•通过压缩缓冲区来腾出空间
        int startIndex = findStartMarker();
        if (startIndex > 0) {
            compactBuffer(startIndex);
            return bufferPosition + required <= dataBuffer.length;
        }
        return false;
    }
    
    /**
     * å¤„理缓冲区中的数据,解析完整数据包
     */
    private static void processBuffer(List<byte[]> completePackets, boolean debugEnabled) {
        final int MIN_PACKET_LENGTH = 9;
        final byte[] START_MARKER = {(byte) 0xDD, (byte) 0xCC};
        while (staticBufferPosition >= MIN_PACKET_LENGTH) {
    private void processBuffer(List<byte[]> completePackets, boolean debugEnabled) {
        while (bufferPosition >= MIN_PACKET_LENGTH) {
            // æŸ¥æ‰¾èµ·å§‹æ ‡è®°
            int startIndex = findStartMarker(START_MARKER);
            int startIndex = findStartMarker();
            if (startIndex == -1) {
                // æ²¡æœ‰æ‰¾åˆ°èµ·å§‹æ ‡è®°ï¼Œæ¸…空无效数据
                if (debugEnabled) {
                    //System.out.println("未找到起始标记,清空缓冲区");
                    System.out.println("未找到起始标记,清空缓冲区");
                }
                staticBufferPosition = 0;
                bufferPosition = 0;
                return;
            }
            
            // æ£€æŸ¥æ˜¯å¦æœ‰è¶³å¤Ÿçš„æ•°æ®è¯»å–数据长度
            if (startIndex + 4 > staticBufferPosition) {
            if (startIndex + 4 > bufferPosition) {
                // æ•°æ®ä¸è¶³ï¼Œç­‰å¾…更多数据
                compactBuffer(startIndex);
                return;
            }
            
            // è¯»å–数据长度 (大端序)
            int dataLength = ((staticDataBuffer[startIndex + 2] & 0xFF) << 8) |
                           (staticDataBuffer[startIndex + 3] & 0xFF);
            int dataLength = ((dataBuffer[startIndex + 2] & 0xFF) << 8) |
                           (dataBuffer[startIndex + 3] & 0xFF);
            int totalPacketLength = 2 + 2 + dataLength + 2; // èµ·å§‹æ ‡è®°2 + æ•°æ®é•¿åº¦2 + æ•°æ®å†…容 + CRC2
            
            // æ£€æŸ¥æ•°æ®é•¿åº¦æœ‰æ•ˆæ€§
            if (dataLength < 0 || totalPacketLength > BUFFER_SIZE) {
                if (debugEnabled) {
                    System.out.println("无效数据长度: " + dataLength + ", è·³è¿‡èµ·å§‹å­—节");
                }
                // è·³è¿‡é”™è¯¯çš„起始标记,继续查找
                compactBuffer(startIndex + 1);
                continue;
            }
            // æ£€æŸ¥æ˜¯å¦æ”¶åˆ°å®Œæ•´æ•°æ®åŒ…
            if (startIndex + totalPacketLength > staticBufferPosition) {
            if (startIndex + totalPacketLength > bufferPosition) {
                // æ•°æ®åŒ…不完整,等待更多数据
                compactBuffer(startIndex);
                return;
            }
            
            // æå–完整数据包
            byte[] packet = new byte[totalPacketLength];
            System.arraycopy(staticDataBuffer, startIndex, packet, 0, totalPacketLength);
            byte[] packet = Arrays.copyOfRange(dataBuffer, startIndex, startIndex + totalPacketLength);
            
            if (debugEnabled) {
                //System.out.println("解析到完整数据包: " + bytesToHex(packet));
                System.out.println("解析到完整数据包: " + bytesToHex(packet));
            }
            
            // æ·»åŠ åˆ°è¿”å›žåˆ—è¡¨
            completePackets.add(packet);
            
            // ç§»åŠ¨ç¼“å†²åŒºä½ç½®
            int remaining = staticBufferPosition - (startIndex + totalPacketLength);
            int remaining = bufferPosition - (startIndex + totalPacketLength);
            if (remaining > 0) {
                System.arraycopy(staticDataBuffer, startIndex + totalPacketLength,
                               staticDataBuffer, 0, remaining);
                System.arraycopy(dataBuffer, startIndex + totalPacketLength,
                               dataBuffer, 0, remaining);
            }
            staticBufferPosition = remaining;
            bufferPosition = remaining;
        }
    }
    
    /**
     * æŸ¥æ‰¾èµ·å§‹æ ‡è®°ä½ç½®
     */
    private static int findStartMarker(byte[] startMarker) {
        for (int i = 0; i <= staticBufferPosition - startMarker.length; i++) {
            if (staticDataBuffer[i] == startMarker[0] && staticDataBuffer[i + 1] == startMarker[1]) {
    private int findStartMarker() {
        for (int i = 0; i <= bufferPosition - START_MARKER.length; i++) {
            if (dataBuffer[i] == START_MARKER[0] && dataBuffer[i + 1] == START_MARKER[1]) {
                return i;
            }
        }
@@ -119,20 +150,20 @@
    /**
     * åŽ‹ç¼©ç¼“å†²åŒºï¼Œå°†æœ‰æ•ˆæ•°æ®ç§»åˆ°å¼€å¤´
     */
    private static void compactBuffer(int startIndex) {
        if (startIndex > 0) {
            System.arraycopy(staticDataBuffer, startIndex, staticDataBuffer, 0,
                           staticBufferPosition - startIndex);
            staticBufferPosition -= startIndex;
    private void compactBuffer(int startIndex) {
        if (startIndex > 0 && startIndex < bufferPosition) {
            System.arraycopy(dataBuffer, startIndex, dataBuffer, 0,
                           bufferPosition - startIndex);
            bufferPosition -= startIndex;
        }
    }
    
    /**
     * æ‰“印原始数据(调试用)
     */
    private static void printRawData(String prefix, byte[] data, int maxPrintLength) {
    private void printRawData(String prefix, byte[] data, int maxPrintLength) {
        if (data == null || data.length == 0) {
            //System.out.println(prefix + ": ç©ºæ•°æ®");
            System.out.println(prefix + ": ç©ºæ•°æ®");
            return;
        }
        
@@ -148,13 +179,13 @@
            sb.append("... [截断,总长度: ").append(data.length).append("]");
        }
        
        //System.out.println(sb.toString());
        System.out.println(sb.toString());
    }
    
    /**
     * å·¥å…·æ–¹æ³•:字节数组转十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
@@ -165,16 +196,23 @@
    /**
     * æ¸…空缓冲区(避免内存泄漏)
     */
    public static void clearBuffer() {
        staticBufferPosition = 0;
    public void clearBuffer() {
        bufferPosition = 0;
        // å¯é€‰ï¼šæ¸…空缓冲区内容
        Arrays.fill(staticDataBuffer, (byte) 0);
        Arrays.fill(dataBuffer, (byte) 0);
    }
    
    /**
     * èŽ·å–å½“å‰ç¼“å†²åŒºçŠ¶æ€
     */
    public static int getBufferStatus() {
        return staticBufferPosition;
    public int getBufferStatus() {
        return bufferPosition;
    }
    /**
     * èŽ·å–ç¼“å†²åŒºå®¹é‡
     */
    public int getBufferCapacity() {
        return dataBuffer.length;
    }
}
src/chuankou/SerialPortService.java
@@ -144,70 +144,83 @@
     * å¯åŠ¨æ•°æ®æŽ¥æ”¶çº¿ç¨‹
     */
    public void startCapture(Consumer<byte[]> onReceived) {
        this.dataReceivedCallback = onReceived;
        if (capturing || port == null || !port.isOpen()) return;
        capturing = true;
        paused = false;
        this.dataReceivedCallback = onReceived;
        if (capturing || port == null || !port.isOpen()) return;
        capturing = true;
        paused = false;
        readerThread = new Thread(() -> {
            buffer.reset();
            long lastReceivedTime = 0;
        readerThread = new Thread(() -> {
            buffer.reset();
            long lastReceivedTime = 0;
            while (capturing && port.isOpen()) {
                long currentTime = System.currentTimeMillis();
            while (capturing && port.isOpen()) {
                long currentTime = System.currentTimeMillis();
                if (buffer.size() > 0 && (currentTime - lastReceivedTime) >= 20) {
                    byte[] data = buffer.toByteArray();
                    SystemDebugDialog.appendHexData(data);
                if (buffer.size() > 0 && (currentTime - lastReceivedTime) >= 20) {
                    byte[] data = buffer.toByteArray();
                    SystemDebugDialog.appendHexData(data);
                    // æ–°å¢žï¼šå°†æ•°æ®ä¼ é€’给协议解析器
                    if (protocolParser != null) {
                        protocolParser.receiveData(data);
                    }
                    // æ–°å¢žï¼šå°†æ•°æ®ä¼ é€’给协议解析器 - ç¡®ä¿å§‹ç»ˆæ‰§è¡Œ
                    if (protocolParser != null) {
                        protocolParser.receiveData(data);
                    }
                    if (!paused) {
                        onReceived.accept(data);
                        if (responseConsumer != null) {
                            responseConsumer.accept(data);
                        }
                    }
                    buffer.reset();
                }
                    // ç¡®ä¿æ•°æ®å›žè°ƒå§‹ç»ˆæ‰§è¡Œï¼Œä¸å—暂停状态影响
                    if (dataReceivedCallback != null && !paused) {
                        dataReceivedCallback.accept(data);
                    }
                    if (responseConsumer != null && !paused) {
                        responseConsumer.accept(data);
                    }
                    buffer.reset();
                }
                int len = port.readBytes(readBuffer, readBuffer.length);
                currentTime = System.currentTimeMillis();
                int len = port.readBytes(readBuffer, readBuffer.length);
                currentTime = System.currentTimeMillis();
                if (len > 0) {
                    buffer.write(readBuffer, 0, len);
                    lastReceivedTime = currentTime;
                }
                if (len > 0) {
                    buffer.write(readBuffer, 0, len);
                    lastReceivedTime = currentTime;
                }
                if (len <= 0 && buffer.size() == 0) {
                    try { Thread.sleep(1); } catch (InterruptedException ignore) {}
                }
            }
                if (len <= 0 && buffer.size() == 0) {
                    try { Thread.sleep(1); } catch (InterruptedException ignore) {}
                }
            }
            if (buffer.size() > 0) {
                byte[] data = buffer.toByteArray();
                SystemDebugDialog.appendHexData(data);
            if (buffer.size() > 0) {
                byte[] data = buffer.toByteArray();
                SystemDebugDialog.appendHexData(data);
                // æ–°å¢žï¼šå°†æ•°æ®ä¼ é€’给协议解析器
                if (protocolParser != null) {
                    protocolParser.receiveData(data);
                }
                // æ–°å¢žï¼šå°†æ•°æ®ä¼ é€’给协议解析器 - ç¡®ä¿å§‹ç»ˆæ‰§è¡Œ
                if (protocolParser != null) {
                    protocolParser.receiveData(data);
                }
                if (!paused) {
                    onReceived.accept(data);
                    if (responseConsumer != null) {
                        responseConsumer.accept(data);
                    }
                }
            }
        });
        readerThread.setDaemon(true);
        readerThread.start();
                // ç¡®ä¿æ•°æ®å›žè°ƒå§‹ç»ˆæ‰§è¡Œï¼Œä¸å—暂停状态影响
                if (dataReceivedCallback != null && !paused) {
                    dataReceivedCallback.accept(data);
                }
                if (responseConsumer != null && !paused) {
                    responseConsumer.accept(data);
                }
            }
        });
        readerThread.setDaemon(true);
        readerThread.start();
    }
    // æ–°å¢žï¼šè®¾ç½®æš‚停状态但不影响协议解析器
    public void setPaused(boolean paused) {
        this.paused = paused;
        // æ³¨æ„ï¼šä¸åœæ­¢åè®®è§£æžå™¨ï¼Œåªæš‚停UI回调
    }
    // æ–°å¢žï¼šå•独停止数据捕获而不影响协议解析器
    public void stopDataCaptureOnly() {
        // åªåœæ­¢æ•°æ®å›žè°ƒï¼Œä¸å½±å“åè®®è§£æžå™¨
        this.dataReceivedCallback = null;
        this.responseConsumer = null;
    }
    /**
     * åœæ­¢æ•°æ®æŽ¥æ”¶çº¿ç¨‹
     */
@@ -219,9 +232,6 @@
        }
    }
    public void setPaused(boolean paused) {
        this.paused = paused;
    }
    public boolean isPaused() {
        return paused;
src/chushihua/Chushihua.java
@@ -2,6 +2,9 @@
import java.io.File;
import java.util.List;
import dialog.Dingshidialog;
import dialog.Errlog;
import home.MachineConfig;
/**
@@ -66,7 +69,7 @@
            
            return true;
        } catch (Exception e) {
            System.err.println("系统初始化异常: " + e.getMessage());
            Errlog.logOperation("系统初始化异常: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
@@ -83,7 +86,7 @@
                ////System.out.println("系统关闭:轮询查询已停止");
            }
        } catch (Exception e) {
            System.err.println("系统关闭异常: " + e.getMessage());
            Errlog.logOperation("系统关闭异常: " + e.getMessage());
        }
    }
    
@@ -96,7 +99,7 @@
            // æ£€æŸ¥é…ç½®æ–‡ä»¶æ˜¯å¦å­˜åœ¨
            File configFile = new File(configFilePath);
            if (!configFile.exists()) {
                System.err.println("配置文件不存在: " + configFilePath);
                Errlog.logOperation("配置文件不存在: " + configFilePath);
                return false;
            }
            
@@ -119,7 +122,7 @@
            return true;
            
        } catch (Exception e) {
            System.err.println("系统初始化失败: " + e.getMessage());
            Errlog.logOperation("系统初始化失败: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
@@ -176,7 +179,7 @@
     */
    public boolean saveConfig(String configFilePath) {
        if (machineConfig == null) {
            System.err.println("配置未加载,无法保存");
            Errlog.logOperation("配置未加载,无法保存");
            return false;
        }
        
@@ -185,7 +188,7 @@
            ////System.out.println("配置已保存到: " + configFilePath);
            return true;
        } catch (Exception e) {
            System.err.println("保存配置失败: " + e.getMessage());
            Errlog.logOperation("保存配置失败: " + e.getMessage());
            return false;
        }
    }
@@ -195,7 +198,7 @@
     */
    public MachineConfig getMachineConfig() {
        if (!initialized) {
            throw new IllegalStateException("系统未初始化,请先调用initialize()方法");
            Dingshidialog.showTimedDialog(null,10,"系统未初始化成功");
        }
        return machineConfig;
    }
src/chushihua/SlotManager.java
@@ -5,7 +5,8 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import xitongshezhi.Fkj;
import dialog.Dingshidialog;
import home.Fkj;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@@ -318,6 +319,7 @@
    /**
     * æ ¹æ®çŠ¶æ€ç å’Œæ•…éšœç åˆ¤æ–­æ˜¯å¦æœ‰å¡ - ä¼˜åŒ–版本
     */
    @SuppressWarnings("unused")
    private String determineHasCardFromStatus(int status, int fault) {
        // ä¼˜åŒ–:使用数值比较替代字符串操作
        if (fault != 0) {
@@ -521,11 +523,10 @@
        if ("0000".equals(oldCardNumber) && !"0000".equals(newCardNumber)) {
            // åœ¨äº‹ä»¶åˆ†å‘线程中显示对话框
            javax.swing.SwingUtilities.invokeLater(() -> {
                xitongshezhi.Dingshidialog.showTimedDialog(
                Dingshidialog.showTimedDialog(
                        null, // çˆ¶çª—口,可以为null
                        5,    // æ˜¾ç¤º3秒
                        "还卡成功,感谢您的使用",
                        ""    // éŸ³é¢‘文件,可以为空
                        slotNumber+"号卡槽还卡成功感谢您的使用"
                        );
            });
            //System.out.println("卡槽 " + slotNumber + " è¿˜å¡æˆåŠŸï¼Œå¡å·ä»Ž " + oldCardNumber + " å˜ä¸º " + newCardNumber);
@@ -538,7 +539,7 @@
     * @param caozuo æ“ä½œç±»åž‹ï¼š1表示管理员,0表示系统
     * @return ä¿®æ”¹æˆåŠŸè¿”å›žtrue,否则返回false
     */
    public static boolean changgehaska(int slotNumber, String caozuo) {
    public static boolean changgehaska(int slotNumber, int caozuo) {
        if (!isValidSlotNumber(slotNumber)) {
            return false;
        }
@@ -548,9 +549,9 @@
        slot.setUpdateTime(getCurrentTime());
        
        // è®°å½•取卡日志
        String operator = "1".equals(caozuo) ? "管理员" : "系统";
        String operator =caozuo==1? "管理员" : "系统";
        String logMessage = String.format("取卡操作:卡槽%d被%s取卡", slotNumber, operator);
        xitongshezhi.Charulog.logOperation(logMessage);
        dialog.Charulog.logOperation(logMessage);
        
        return true;
    }
src/chushihua/lunxun.java
@@ -1,11 +1,13 @@
package chushihua;
import chuankou.Sendmsg;
import dialog.Errlog;
import home.Fkj;
import home.MachineConfig;
import publicway.QueryData;
import xitongshezhi.Fkj;
import xitongshezhi.SystemDebugDialog;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -14,637 +16,785 @@
 * è½®è¯¢æŸ¥è¯¢ç±» - ä¼˜åŒ–版本
 * ç”¨äºŽå®šæ—¶å‘所有卡槽发送查询指令
 * æ”¯æŒæš‚停和恢复功能,检查串口连接状态
 * æ–°å¢žï¼šä¸åŒå¡æ§½çŠ¶æ€ä½¿ç”¨ä¸åŒæŸ¥è¯¢é¢‘çŽ‡
 * ä¼˜åŒ–:内存管理和长时间运行稳定性
 */
public class lunxun {
    private static volatile boolean isRunning = false;
    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 final int MIN_SLOT = 1;
    private static final int MAX_SLOT = 60;
    // ä¸²å£è¿žæŽ¥æ£€æŸ¥ç›¸å…³
    private static final int SERIAL_CHECK_INTERVAL = 5000; // 5秒检查一次串口连接
    private static long lastSerialCheckTime = 0;
    private static boolean serialConnected = false;
    // æ€§èƒ½ä¼˜åŒ–:查询指令缓存
    private static final Map<Integer, String> queryCommandCache = new ConcurrentHashMap<>();
    // è°ƒè¯•模式控制
    public static  boolean DEBUG_ENABLED = false;
    /**
     * æ£€æŸ¥ä¸²å£è¿žæŽ¥çŠ¶æ€ - ä¼˜åŒ–版本,添加重试机制
     * @return true-串口已连接, false-串口未连接
     */
    public static boolean checkSerialConnection() {
        // é¿å…é¢‘繁检查,每5秒检查一次
        long currentTime = System.currentTimeMillis();
        if (currentTime - lastSerialCheckTime < SERIAL_CHECK_INTERVAL) {
            return serialConnected;
        }
        lastSerialCheckTime = currentTime;
        try {
            // ç®€åŒ–检查:直接检查Sendmsg的串口状态,而不是发送测试指令
            boolean result = Sendmsg.isPortOpen();
            if (result) {
                if (DEBUG_ENABLED) {
                    //System.out.println("串口连接正常");
                }
                serialConnected = true;
            } else {
                System.err.println("串口连接失败 - ä¸²å£æœªæ‰“å¼€");
                serialConnected = false;
            }
        } catch (Exception e) {
            System.err.println("串口连接检查异常: " + e.getMessage());
            serialConnected = false;
        }
        return serialConnected;
    }
    /**
     * å¸¦é‡è¯•的串口连接检查
     */
    private static boolean checkSerialConnectionWithRetry() {
        for (int i = 0; i < 3; i++) {
            if (checkSerialConnection()) {
                return true;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        }
        return false;
    }
    /**
     * å¯åŠ¨è½®è¯¢æŸ¥è¯¢ - ä¼˜åŒ–版本
     * @return true-启动成功, false-启动失败
     */
    public static boolean startPolling() {
        if (isRunning) {
            //System.out.println("轮询查询已经在运行中");
            return true;
        }
        // å¯åŠ¨å‰ä¸¥æ ¼æ£€æŸ¥ä¸²å£è¿žæŽ¥
        if (!checkSerialConnectionWithRetry()) {
            System.err.println("串口未连接,无法启动轮询查询");
            return false;
        }
        // ä»Žé…ç½®ä¸­èŽ·å–è½®è¯¢é—´éš”
        loadPollingIntervalFromConfig();
        isRunning = true;
        isPaused = false;
        shouldStop.set(false);
        try {
            pollingThread = new Thread(new PollingTask(), "CardSlot-Polling-Thread");
            pollingThread.setDaemon(true);
            pollingThread.start();
            return true;
        } catch (Exception e) {
            System.err.println("启动轮询查询线程时发生异常: " + e.getMessage());
            isRunning = false;
            shouldStop.set(true);
            return false;
        }
    }
    /**
     * åœæ­¢è½®è¯¢æŸ¥è¯¢ - ä¿®å¤ç‰ˆæœ¬
     * @return true-停止成功, false-停止失败
     */
    public static boolean stopPolling() {
        if (!isRunning) {
            //System.out.println("轮询查询未在运行");
            return false;
        }
        shouldStop.set(true);
        isRunning = false;
        isPaused = false;
        if (pollingThread != null) {
            pollingThread.interrupt();
            try {
                pollingThread.join(3000); // ç­‰å¾…3秒
                // æ£€æŸ¥çº¿ç¨‹æ˜¯å¦è¿˜åœ¨è¿è¡Œ
                if (pollingThread.isAlive()) {
                    System.err.println("轮询线程未在3秒内停止,标记为守护线程并忽略");
                    // ä¸å¼ºåˆ¶åœæ­¢ï¼Œè€Œæ˜¯ç¡®ä¿å®ƒæ˜¯å®ˆæŠ¤çº¿ç¨‹
                    pollingThread.setDaemon(true);
                }
            } catch (InterruptedException e) {
                System.err.println("停止轮询查询时被中断: " + e.getMessage());
                Thread.currentThread().interrupt();
            } catch (Exception e) {
                System.err.println("停止轮询线程时发生异常: " + e.getMessage());
            } finally {
                pollingThread = null;
            }
        }
        shouldStop.set(false);
        //System.out.println("轮询查询已停止");
        return true;
    }
    /**
     * æš‚停轮询查询
     * @return true-暂停成功, false-暂停失败
     */
    public static boolean pausePolling() {
        if (!isRunning) {
            if (DEBUG_ENABLED) {
                //System.out.println("轮询查询未在运行,无法暂停");
            }
            return false;
        }
        if (isPaused) {
            if (DEBUG_ENABLED) {
                //System.out.println("轮询查询已经处于暂停状态");
            }
            return false;
        }
        isPaused = true;
        //System.out.println("轮询查询已暂停");
        return true;
    }
    /**
     * æ¢å¤è½®è¯¢æŸ¥è¯¢
     * @return true-恢复成功, false-恢复失败
     */
    public static boolean resumePolling() {
        if (!isRunning) {
            //System.out.println("轮询查询未在运行,无法恢复");
            return false;
        }
        if (!isPaused) {
            //System.out.println("轮询查询未处于暂停状态");
            return false;
        }
        // æ¢å¤å‰æ£€æŸ¥ä¸²å£è¿žæŽ¥
        if (!checkSerialConnectionWithRetry()) {
            System.err.println("串口未连接,无法恢复轮询查询");
            return false;
        }
        isPaused = false;
        synchronized (lunxun.class) {
            lunxun.class.notifyAll(); // å”¤é†’等待的线程
        }
        //System.out.println("轮询查询已恢复");
        return true;
    }
    /**
     * æ£€æŸ¥è½®è¯¢çŠ¶æ€
     * @return true-正在运行, false-已停止
     */
    public static boolean isPolling() {
        return isRunning;
    }
    /**
     * æ£€æŸ¥æ˜¯å¦æš‚停
     * @return true-已暂停, false-未暂停
     */
    public static boolean isPaused() {
        return isPaused;
    }
    /**
     * æ£€æŸ¥ä¸²å£è¿žæŽ¥çŠ¶æ€
     * @return true-串口已连接, false-串口未连接
     */
    public static boolean isSerialConnected() {
        return serialConnected;
    }
    /**
     * è®¾ç½®è½®è¯¢é—´éš”
     * @param interval è½®è¯¢é—´éš”(毫秒)
     */
    public static void setPollingInterval(int interval) {
        if (interval < 10) {
            System.err.println("轮询间隔不能小于10ms");
            return;
        }
        pollingInterval = interval;
        //System.out.println("轮询间隔已设置为: " + interval + "ms");
        // å¦‚果正在运行,重新启动以应用新的间隔
        if (isRunning) {
            restartPolling();
        }
    }
    /**
     * èŽ·å–å½“å‰è½®è¯¢é—´éš”
     * @return è½®è¯¢é—´éš”(毫秒)
     */
    public static int getPollingInterval() {
        return pollingInterval;
    }
    /**
     * é‡æ–°å¯åŠ¨è½®è¯¢æŸ¥è¯¢
     * @return true-重启成功, false-重启失败
     */
    public static boolean restartPolling() {
        stopPolling();
        // ç­‰å¾…一小段时间确保线程完全停止
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return startPolling();
    }
    /**
     * ä»Žé…ç½®ä¸­åŠ è½½è½®è¯¢é—´éš”
     */
    private static void loadPollingIntervalFromConfig() {
        try {
            Chushihua configSystem = Chushihua.getInstance();
            if (configSystem.isInitialized()) {
                MachineConfig machineConfig = configSystem.getMachineConfig();
                pollingInterval = machineConfig.getPollingInterval();
                //System.out.println("从配置加载轮询间隔: " + pollingInterval + "ms");
            } else {
                //System.out.println("配置系统未初始化,使用默认轮询间隔: " + pollingInterval + "ms");
            }
        } catch (Exception e) {
            System.err.println("加载轮询间隔配置失败: " + e.getMessage());
            //System.out.println("使用默认轮询间隔: " + pollingInterval + "ms");
        }
    }
    /**
     * èŽ·å–ç¼“å­˜çš„æŸ¥è¯¢æŒ‡ä»¤
     */
    private static String getCachedQueryCommand(int slotNumber) {
        return queryCommandCache.computeIfAbsent(slotNumber, QueryData::queryData);
    }
    /**
     * æ¸…空查询指令缓存(当查询逻辑变化时调用)
     */
    public static void clearQueryCache() {
        queryCommandCache.clear();
        //System.out.println("查询指令缓存已清空");
    }
    /**
     * è½®è¯¢ä»»åŠ¡å†…éƒ¨ç±» - ä¼˜åŒ–版本
     * ä¼˜åŒ–内存使用:避免在循环中创建新对象
     * æ·»åŠ æ‰¹é‡å¤„ç†å’Œæ€§èƒ½ç›‘æŽ§
     */
    private static class PollingTask implements Runnable {
        private int currentIndex = 0; // å½“前索引,用于遍历slotArray
        private int consecutiveFailures = 0; // è¿žç»­å¤±è´¥æ¬¡æ•°
        private static final int MAX_CONSECUTIVE_FAILURES = 5; // æœ€å¤§è¿žç»­å¤±è´¥æ¬¡æ•°
        @Override
        public void run() {
            //System.out.println("轮询查询线程开始运行");
            while (isRunning && !Thread.currentThread().isInterrupted() && !shouldStop.get()) {
                try {
                    // æ£€æŸ¥æ˜¯å¦æš‚停
                    if (isPaused) {
                        synchronized (lunxun.class) {
                            while (isPaused && isRunning && !shouldStop.get()) {
                                lunxun.class.wait(1000); // ç­‰å¾…1秒或直到被唤醒
                            }
                        }
                        continue;
                    }
                    // å®šæœŸæ£€æŸ¥ä¸²å£è¿žæŽ¥çŠ¶æ€ï¼ˆæ¯10次循环检查一次)
                    if (currentIndex % 10 == 0 && !checkSerialConnectionWithRetry()) {
                        System.err.println("串口连接断开,暂停轮询");
                        pausePolling();
                        continue;
                    }
                    // èŽ·å–å¡æ§½æ•°ç»„
                    Fkj[] slotArray = SlotManager.getSlotArray();
                    if (slotArray == null || slotArray.length == 0) {
                        System.err.println("卡槽数组未初始化");
                        Thread.sleep(pollingInterval);
                        continue;
                    }
                    // éåŽ†æ‰€æœ‰å¡æ§½ï¼Œåªç»™ hasCard != 1 çš„卡槽发送查询指令
                    boolean sentQuery = false;
                    int checkedSlots = 0;
                    int maxSlotsPerCycle = Math.min(10, slotArray.length); // æ¯å‘¨æœŸæœ€å¤šæ£€æŸ¥10个卡槽
                    for (int i = 0; i < maxSlotsPerCycle && checkedSlots < slotArray.length; i++) {
                        Fkj slot = slotArray[currentIndex];
                        if (slot != null) {
                            String hasCard = slot.getHasCard();
                            if (!"1".equals(hasCard)) {
                                int slotNumber = currentIndex + 1;
                                if (sendQueryToSlot(slotNumber)) {
                                    sentQuery = true;
                                    consecutiveFailures = 0;
                                    // å…³é”®ä¿®å¤ï¼šåœ¨break前先更新索引
                                    currentIndex = (currentIndex + 1) % slotArray.length;
                                    checkedSlots++;
                                    Thread.sleep(pollingInterval);
                                    break;
                                } else {
                                    consecutiveFailures++;
                                    if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
                                        System.err.println("lunxun连续失败次数过多,暂停轮询");
                                        pausePolling();
                                        break;
                                    }
                                }
                            }
                        }
                        // å¯¹äºŽä¸éœ€è¦å‘送查询的卡槽,正常更新索引
                        currentIndex = (currentIndex + 1) % slotArray.length;
                        checkedSlots++;
                    }
                    // å¦‚果没有找到需要查询的卡槽,等待一段时间再继续
                    if (!sentQuery) {
                        Thread.sleep(pollingInterval * 2); // æ²¡æœ‰æŸ¥è¯¢æ—¶ç­‰å¾…时间加倍
                    }
                } catch (InterruptedException e) {
                    //System.out.println("轮询查询线程被中断");
                    Thread.currentThread().interrupt();
                    break;
                } catch (Exception e) {
                    System.err.println("轮询查询过程中发生异常: " + e.getMessage());
                    consecutiveFailures++;
                    // å‘生异常时等待一段时间再继续
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
            //System.out.println("轮询查询线程结束运行");
        }
        /**
         * å‘指定卡槽发送查询指令 - ä¼˜åŒ–版本
         * ä½¿ç”¨ç¼“存指令,优化调试输出
         */
        private boolean sendQueryToSlot(int slotNumber) {
            try {
                // ä½¿ç”¨ç¼“存的查询指令
                String queryCommand = getCachedQueryCommand(slotNumber);
                if (queryCommand != null && !queryCommand.trim().isEmpty()) {
                    // å‘送到串口
                    boolean sendResult = Sendmsg.sendMessage(queryCommand);
                    if (sendResult) {
                        // åªåœ¨è°ƒè¯•时输出,避免频繁打印
                        if (DEBUG_ENABLED) {
                            SystemDebugDialog.appendAsciiData(String.format("Slot %d Send query (hasCard !=1)\n", slotNumber));
                        }
                        return true;
                    } else {
                        if (DEBUG_ENABLED) {
                            SystemDebugDialog.appendAsciiData("Send query command to card slot err");
                        }
                        // å‘送失败可能是串口断开,更新连接状态
                        serialConnected = false;
                        return false;
                    }
                } else {
                    System.err.println("生成的查询指令为空,卡槽: " + slotNumber);
                    return false;
                }
            } catch (Exception e) {
                System.err.println("发送查询指令到卡槽 " + slotNumber + " æ—¶å‘生异常: " + e.getMessage());
                // å‘生异常时更新串口连接状态
                serialConnected = false;
                return false;
            }
        }
    }
    /**
     * ç«‹å³å‘指定卡槽发送查询指令(不等待轮询)
     * @param slotNumber å¡æ§½ç¼–号 (1-60)
     * @return true-发送成功, false-发送失败
     */
    public static boolean sendImmediateQuery(int slotNumber) {
        if (slotNumber < MIN_SLOT || slotNumber > MAX_SLOT) {
            System.err.println("卡槽编号必须在" + MIN_SLOT + "-" + MAX_SLOT + "之间");
            return false;
        }
        // æ£€æŸ¥ä¸²å£è¿žæŽ¥
        if (!checkSerialConnectionWithRetry()) {
            System.err.println("串口未连接,无法发送查询指令");
            return false;
        }
        try {
            // ä½¿ç”¨ç¼“存的查询指令
            String queryCommand = getCachedQueryCommand(slotNumber);
            if (queryCommand != null && !queryCommand.trim().isEmpty()) {
                // å‘送到串口
                boolean sendResult = Sendmsg.sendMessage(queryCommand);
                if (sendResult) {
                    if (DEBUG_ENABLED) {
                        //System.out.println("立即查询成功 - å¡æ§½ " + slotNumber);
                    }
                    return true;
                } else {
                    System.err.println("立即查询失败 - å‘送指令到卡槽 " + slotNumber + " å¤±è´¥");
                    return false;
                }
            } else {
                System.err.println("立即查询失败 - ç”Ÿæˆçš„æŸ¥è¯¢æŒ‡ä»¤ä¸ºç©ºï¼Œå¡æ§½: " + slotNumber);
                return false;
            }
        } catch (Exception e) {
            System.err.println("立即查询卡槽 " + slotNumber + " æ—¶å‘生异常: " + e.getMessage());
            return false;
        }
    }
    /**
     * ç«‹å³å‘所有卡槽发送查询指令(批量)- ä¼˜åŒ–版本
     * @return æˆåŠŸå‘é€çš„æŒ‡ä»¤æ•°é‡
     */
    public static int sendImmediateQueryToAll() {
        // æ£€æŸ¥ä¸²å£è¿žæŽ¥
        if (!checkSerialConnectionWithRetry()) {
            System.err.println("串口未连接,无法发送批量查询指令");
            return 0;
        }
        int successCount = 0;
        int batchSize = 5; // æ¯æ‰¹æ¬¡å‘送5个查询
        int totalSlots = MAX_SLOT - MIN_SLOT + 1;
        //System.out.println("开始批量查询所有卡槽...");
        for (int batchStart = MIN_SLOT; batchStart <= MAX_SLOT; batchStart += batchSize) {
            if (shouldStop.get()) {
                break;
            }
            int batchEnd = Math.min(batchStart + batchSize - 1, MAX_SLOT);
            // æ‰¹æ¬¡å†…查询
            for (int slot = batchStart; slot <= batchEnd; slot++) {
                if (sendImmediateQuery(slot)) {
                    successCount++;
                }
            }
            // æ‰¹æ¬¡é—´é—´éš”,避免串口拥堵
            if (batchEnd < MAX_SLOT) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
        //System.out.println("批量查询完成,成功发送: " + successCount + "/" + totalSlots);
        return successCount;
    }
    /**
     * æ‰‹åŠ¨è®¾ç½®ä¸²å£è¿žæŽ¥çŠ¶æ€ï¼ˆç”¨äºŽå¤–éƒ¨æ£€æµ‹åˆ°ä¸²å£çŠ¶æ€å˜åŒ–æ—¶è°ƒç”¨ï¼‰
     * @param connected ä¸²å£è¿žæŽ¥çŠ¶æ€
     */
    public static void setSerialConnected(boolean connected) {
        serialConnected = connected;
        lastSerialCheckTime = System.currentTimeMillis();
        if (connected) {
//            //System.out.println("串口连接状态已设置为: å·²è¿žæŽ¥");
        } else {
            System.err.println("串口连接状态已设置为: æœªè¿žæŽ¥");
            // å¦‚果串口断开且轮询正在运行,自动暂停轮询
            if (isRunning && !isPaused) {
                pausePolling();
            }
        }
    }
    /**
     * èŽ·å–è½®è¯¢çŠ¶æ€ä¿¡æ¯
     * @return çŠ¶æ€ä¿¡æ¯å­—ç¬¦ä¸²
     */
    public static String getPollingStatus() {
        String status;
        if (!isRunning) {
            status = "已停止";
        } else if (isPaused) {
            status = "已暂停";
        } else {
            status = "运行中";
        }
        String serialStatus = serialConnected ? "已连接" : "未连接";
        int cacheSize = queryCommandCache.size();
        return String.format("轮询状态: %s, ä¸²å£: %s, é—´éš”: %dms, æŒ‡ä»¤ç¼“å­˜: %d, å¡æ§½èŒƒå›´: %d-%d",
                           status, serialStatus, pollingInterval, cacheSize, MIN_SLOT, MAX_SLOT);
    }
    /**
     * ç›´æŽ¥è®¾ç½®è½®è¯¢æš‚停状态(供其他类调用)
     * @param paused true-暂停轮询, false-恢复轮询
     * @return true-设置成功, false-设置失败(如轮询未运行)
     */
    public static boolean setPollingPaused(boolean paused) {
        if (!isRunning) {
            //System.out.println("轮询查询未在运行,无法设置暂停状态");
            return false;
        }
        if (paused) {
            // è¯·æ±‚暂停
            if (!isPaused) {
                isPaused = true;
                //System.out.println("轮询查询已通过外部调用暂停");
                return true;
            } else {
                //System.out.println("轮询查询已经处于暂停状态");
                return false;
            }
        } else {
            // è¯·æ±‚恢复
            if (isPaused) {
                // æ¢å¤å‰æ£€æŸ¥ä¸²å£è¿žæŽ¥
                if (!checkSerialConnectionWithRetry()) {
                    System.err.println("串口未连接,无法恢复轮询查询");
                    return false;
                }
                isPaused = false;
                synchronized (lunxun.class) {
                    lunxun.class.notifyAll(); // å”¤é†’等待的线程
                }
                //System.out.println("轮询查询已通过外部调用恢复");
                return true;
            } else {
                //System.out.println("轮询查询未处于暂停状态");
                return false;
            }
        }
    }
    /**
     * èŽ·å–æ€§èƒ½ç»Ÿè®¡ä¿¡æ¯
     */
    public static String getPerformanceStats() {
        return String.format("查询指令缓存大小: %d, è½®è¯¢é—´éš”: %dms",
                           queryCommandCache.size(), pollingInterval);
    }
    private static volatile boolean isRunning = false;
    private static volatile boolean isPaused = false;
    private static final AtomicBoolean shouldStop = new AtomicBoolean(false);
    private static Thread pollingThread;
    private static int pollingInterval = 100; // é»˜è®¤è½®è¯¢é—´éš”
    public static boolean sendChaxunzhiling=true;//是否向串口发送查询指令
    // å¡æ§½ç›¸å…³å¸¸é‡
    private static final int MIN_SLOT = 1;
    private static final int MAX_SLOT = 60;
    // ä¸²å£è¿žæŽ¥æ£€æŸ¥ç›¸å…³
    private static final int SERIAL_CHECK_INTERVAL = 5000; // 5秒检查一次串口连接
    private static long lastSerialCheckTime = 0;
    private static boolean serialConnected = false;
    // æ€§èƒ½ä¼˜åŒ–:查询指令缓存
    private static final Map<Integer, String> queryCommandCache = new ConcurrentHashMap<>();
    // å†…存优化:缓存大小限制和清理机制
    private static final int MAX_CACHE_SIZE = 100;
    private static final long CACHE_CLEANUP_INTERVAL = 60000; // 1分钟清理一次
    private static long lastCleanupTime = 0;
    // é”™è¯¯æ—¥å¿—限流机制
    private static final Map<String, Long> lastErrorLogTime = new ConcurrentHashMap<>();
    private static final long ERROR_LOG_INTERVAL = 5000; // ç›¸åŒé”™è¯¯5秒内只记录一次
    // è°ƒè¯•模式控制
    public static  boolean DEBUG_ENABLED = false;
    // æ–°å¢žï¼šä¸åŒçŠ¶æ€å¡æ§½çš„æŸ¥è¯¢é¢‘çŽ‡æŽ§åˆ¶
    private static final int NO_CARD_QUERY_INTERVAL = 100;    // æ— å¡å¡æ§½æŸ¥è¯¢é—´éš”:100ms
    private static final int HAS_CARD_QUERY_INTERVAL = 10000; // æœ‰å¡å¡æ§½æŸ¥è¯¢é—´éš”:10秒
    private static final Map<Integer, Long> lastQueryTimeMap = new ConcurrentHashMap<>(); // è®°å½•每个卡槽的最后查询时间
    public static boolean isSendChaxunzhiling() {
        return sendChaxunzhiling;
    }
    public static void setSendChaxunzhiling(boolean sendChaxunzhiling) {
        lunxun.sendChaxunzhiling = sendChaxunzhiling;
    }
    /**
     * æ£€æŸ¥ä¸²å£è¿žæŽ¥çŠ¶æ€ - ä¼˜åŒ–版本,添加重试机制
     * @return true-串口已连接, false-串口未连接
     */
    public static boolean checkSerialConnection() {
        // é¿å…é¢‘繁检查,每5秒检查一次
        long currentTime = System.currentTimeMillis();
        if (currentTime - lastSerialCheckTime < SERIAL_CHECK_INTERVAL) {
            return serialConnected;
        }
        lastSerialCheckTime = currentTime;
        try {
            // ç®€åŒ–检查:直接检查Sendmsg的串口状态,而不是发送测试指令
            boolean result = Sendmsg.isPortOpen();
            if (result) {
                if (DEBUG_ENABLED) {
                    //System.out.println("串口连接正常");
                }
                serialConnected = true;
            } else {
                logErrorWithRateLimit("serial_connection_failed", "串口连接失败 - ä¸²å£æœªæ‰“å¼€");
                serialConnected = false;
            }
        } catch (Exception e) {
            logErrorWithRateLimit("serial_connection_exception", "串口连接检查异常: " + e.getMessage());
            serialConnected = false;
        }
        return serialConnected;
    }
    /**
     * å¸¦é‡è¯•的串口连接检查
     */
    private static boolean checkSerialConnectionWithRetry() {
        for (int i = 0; i < 3; i++) {
            if (checkSerialConnection()) {
                return true;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        }
        return false;
    }
    /**
     * å¯åŠ¨è½®è¯¢æŸ¥è¯¢ - ä¼˜åŒ–版本
     * @return true-启动成功, false-启动失败
     */
    public static boolean startPolling() {
        if (isRunning) {
            //System.out.println("轮询查询已经在运行中");
            return true;
        }
        // å¯åŠ¨å‰ä¸¥æ ¼æ£€æŸ¥ä¸²å£è¿žæŽ¥
        if (!checkSerialConnectionWithRetry()) {
            logErrorWithRateLimit("start_polling_serial_failed", "串口未连接,无法启动轮询查询");
            return false;
        }
        // å¯åŠ¨å‰å…ˆæ¸…ç†ä¸€æ¬¡å†…å­˜
        performCleanup();
        // ä»Žé…ç½®ä¸­èŽ·å–è½®è¯¢é—´éš”
        loadPollingIntervalFromConfig();
        // åˆå§‹åŒ–最后查询时间记录
        initializeLastQueryTimes();
        isRunning = true;
        isPaused = false;
        shouldStop.set(false);
        try {
            pollingThread = new Thread(new PollingTask(), "CardSlot-Polling-Thread");
            pollingThread.setDaemon(true);
            pollingThread.start();
            return true;
        } catch (Exception e) {
            logErrorWithRateLimit("start_polling_thread_exception", "启动轮询查询线程时发生异常: " + e.getMessage());
            isRunning = false;
            shouldStop.set(true);
            return false;
        }
    }
    /**
     * åˆå§‹åŒ–最后查询时间记录
     */
    private static void initializeLastQueryTimes() {
        lastQueryTimeMap.clear();
        for (int i = MIN_SLOT; i <= MAX_SLOT; i++) {
            lastQueryTimeMap.put(i, 0L); // åˆå§‹åŒ–为0,表示从未查询过
        }
    }
    /**
     * åœæ­¢è½®è¯¢æŸ¥è¯¢ - ä¿®å¤ç‰ˆæœ¬
     * @return true-停止成功, false-停止失败
     */
    public static boolean stopPolling() {
        if (!isRunning) {
            //System.out.println("轮询查询未在运行");
            return false;
        }
        shouldStop.set(true);
        isRunning = false;
        isPaused = false;
        if (pollingThread != null) {
            pollingThread.interrupt();
            try {
                pollingThread.join(3000); // ç­‰å¾…3秒
                // æ£€æŸ¥çº¿ç¨‹æ˜¯å¦è¿˜åœ¨è¿è¡Œ
                if (pollingThread.isAlive()) {
                    logErrorWithRateLimit("polling_thread_stop_timeout", "轮询线程未在3秒内停止,标记为守护线程并忽略");
                    // ä¸å¼ºåˆ¶åœæ­¢ï¼Œè€Œæ˜¯ç¡®ä¿å®ƒæ˜¯å®ˆæŠ¤çº¿ç¨‹
                    pollingThread.setDaemon(true);
                }
            } catch (InterruptedException e) {
                logErrorWithRateLimit("stop_polling_interrupted", "停止轮询查询时被中断: " + e.getMessage());
                Thread.currentThread().interrupt();
            } catch (Exception e) {
                logErrorWithRateLimit("stop_polling_exception", "停止轮询线程时发生异常: " + e.getMessage());
            } finally {
                pollingThread = null;
            }
        }
        shouldStop.set(false);
        //System.out.println("轮询查询已停止 - ä¸²å£æ•°æ®æŽ¥æ”¶ä¸å—影响");
        return true;
    }
    /**
     * æš‚停轮询查询
     * @return true-暂停成功, false-暂停失败
     */
    public static boolean pausePolling() {
        if (!isRunning) {
            if (DEBUG_ENABLED) {
                //System.out.println("轮询查询未在运行,无法暂停");
            }
            return false;
        }
        if (isPaused) {
            if (DEBUG_ENABLED) {
                //System.out.println("轮询查询已经处于暂停状态");
            }
            return false;
        }
        isPaused = true;
        //System.out.println("轮询查询已暂停 - ä»…停止发送查询指令");
        return true;
    }
    /**
     * æ¢å¤è½®è¯¢æŸ¥è¯¢
     * @return true-恢复成功, false-恢复失败
     */
    public static boolean resumePolling() {
        if (!isRunning) {
            //System.out.println("轮询查询未在运行,无法恢复");
            return false;
        }
        if (!isPaused) {
            //System.out.println("轮询查询未处于暂停状态");
            return false;
        }
        // æ¢å¤å‰æ£€æŸ¥ä¸²å£è¿žæŽ¥
        if (!checkSerialConnectionWithRetry()) {
            logErrorWithRateLimit("resume_polling_serial_failed", "串口未连接,无法恢复轮询查询");
            return false;
        }
        isPaused = false;
        synchronized (lunxun.class) {
            lunxun.class.notifyAll(); // å”¤é†’等待的线程
        }
        //System.out.println("轮询查询已恢复");
        return true;
    }
    /**
     * æ£€æŸ¥è½®è¯¢çŠ¶æ€
     * @return true-正在运行, false-已停止
     */
    public static boolean isPolling() {
        return isRunning;
    }
    /**
     * æ£€æŸ¥æ˜¯å¦æš‚停
     * @return true-已暂停, false-未暂停
     */
    public static boolean isPaused() {
        return isPaused;
    }
    /**
     * æ£€æŸ¥ä¸²å£è¿žæŽ¥çŠ¶æ€
     * @return true-串口已连接, false-串口未连接
     */
    public static boolean isSerialConnected() {
        return serialConnected;
    }
    /**
     * è®¾ç½®è½®è¯¢é—´éš”
     * @param interval è½®è¯¢é—´éš”(毫秒)
     */
    public static void setPollingInterval(int interval) {
        if (interval < 10) {
            logErrorWithRateLimit("polling_interval_too_small", "轮询间隔不能小于10ms");
            return;
        }
        pollingInterval = interval;
        //System.out.println("轮询间隔已设置为: " + interval + "ms");
        // å¦‚果正在运行,重新启动以应用新的间隔
        if (isRunning) {
            restartPolling();
        }
    }
    /**
     * èŽ·å–å½“å‰è½®è¯¢é—´éš”
     * @return è½®è¯¢é—´éš”(毫秒)
     */
    public static int getPollingInterval() {
        return pollingInterval;
    }
    /**
     * é‡æ–°å¯åŠ¨è½®è¯¢æŸ¥è¯¢
     * @return true-重启成功, false-重启失败
     */
    public static boolean restartPolling() {
        stopPolling();
        // ç­‰å¾…一小段时间确保线程完全停止
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return startPolling();
    }
    /**
     * ä»Žé…ç½®ä¸­åŠ è½½è½®è¯¢é—´éš”
     */
    private static void loadPollingIntervalFromConfig() {
        try {
            Chushihua configSystem = Chushihua.getInstance();
            if (configSystem.isInitialized()) {
                MachineConfig machineConfig = configSystem.getMachineConfig();
                pollingInterval = machineConfig.getPollingInterval();
                //System.out.println("从配置加载轮询间隔: " + pollingInterval + "ms");
            } else {
                //System.out.println("配置系统未初始化,使用默认轮询间隔: " + pollingInterval + "ms");
            }
        } catch (Exception e) {
            logErrorWithRateLimit("load_polling_interval_failed", "加载轮询间隔配置失败: " + e.getMessage());
            //System.out.println("使用默认轮询间隔: " + pollingInterval + "ms");
        }
    }
    /**
     * èŽ·å–ç¼“å­˜çš„æŸ¥è¯¢æŒ‡ä»¤
     */
    private static String getCachedQueryCommand(int slotNumber) {
        return queryCommandCache.computeIfAbsent(slotNumber, QueryData::queryData);
    }
    /**
     * æ¸…空查询指令缓存(当查询逻辑变化时调用)
     */
    public static void clearQueryCache() {
        queryCommandCache.clear();
        //System.out.println("查询指令缓存已清空");
    }
    /**
     * è½®è¯¢ä»»åŠ¡å†…éƒ¨ç±» - ä¼˜åŒ–版本
     * æ”¯æŒä¸åŒçŠ¶æ€å¡æ§½çš„ä¸åŒæŸ¥è¯¢é¢‘çŽ‡
     * ä¼˜åŒ–:内存管理和重用对象
     */
    private static class PollingTask implements Runnable {
        private int currentIndex = 0; // å½“前索引,用于遍历slotArray
        private int consecutiveFailures = 0; // è¿žç»­å¤±è´¥æ¬¡æ•°
        private static final int MAX_CONSECUTIVE_FAILURES = 5; // æœ€å¤§è¿žç»­å¤±è´¥æ¬¡æ•°
        private final StringBuilder debugBuilder = new StringBuilder(100); // é‡ç”¨ StringBuilder
        @Override
        public void run() {
            //System.out.println("轮询查询线程开始运行");
            while (isRunning && !Thread.currentThread().isInterrupted() && !shouldStop.get()) {
                try {
//                    System.out.println("查询中.....线程");
                    // æ£€æŸ¥æ˜¯å¦æš‚停
                    if (isPaused) {
                        synchronized (lunxun.class) {
                            while (isPaused && isRunning && !shouldStop.get()) {
                                lunxun.class.wait(1000); // ç­‰å¾…1秒或直到被唤醒
                            }
                        }
                        continue;
                    }
                    // å®šæœŸæ£€æŸ¥ä¸²å£è¿žæŽ¥çŠ¶æ€ï¼ˆæ¯10次循环检查一次)
                    if (currentIndex % 10 == 0 && !checkSerialConnectionWithRetry()) {
                        logErrorWithRateLimit("serial_disconnected", "串口连接断开,暂停轮询");
                        pausePolling();
                        continue;
                    }
                    // å®šæœŸæ¸…理缓存(每100次循环清理一次)
                    if (currentIndex % 100 == 0) {
                        cleanupOldCache();
                    }
                    // èŽ·å–å¡æ§½æ•°ç»„
                    Fkj[] slotArray = SlotManager.getSlotArray();
                    if (slotArray == null || slotArray.length == 0) {
                        logErrorWithRateLimit("slot_array_not_initialized", "卡槽数组未初始化");
                        Thread.sleep(pollingInterval);
                        continue;
                    }
                    // æ–°å¢žï¼šæ ¹æ®å¡æ§½çŠ¶æ€å’ŒæŸ¥è¯¢é¢‘çŽ‡å†³å®šæ˜¯å¦å‘é€æŸ¥è¯¢
                    boolean sentQuery = false;
                    long currentTime = System.currentTimeMillis();
                    // éåŽ†å¡æ§½ï¼Œå¯»æ‰¾éœ€è¦æŸ¥è¯¢çš„å¡æ§½
                    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);
                            // ç¡®å®šæŸ¥è¯¢é—´éš”:只有hasCard="1"的卡槽使用10秒间隔,其他情况(包括"-1")都使用100ms间隔
                            int queryInterval = "1".equals(hasCard) ? HAS_CARD_QUERY_INTERVAL : NO_CARD_QUERY_INTERVAL;
                            // æ£€æŸ¥æ˜¯å¦è¾¾åˆ°æŸ¥è¯¢æ—¶é—´
                            if (lastQueryTime == null || currentTime - lastQueryTime >= queryInterval) {
                                if (sendQueryToSlot(slotNumber)) {
                                    // æ›´æ–°æœ€åŽæŸ¥è¯¢æ—¶é—´
                                    lastQueryTimeMap.put(slotNumber, currentTime);
                                    sentQuery = true;
                                    consecutiveFailures = 0;
                                    // æˆåŠŸå‘é€æŸ¥è¯¢åŽï¼Œç­‰å¾…100ms再继续下一个查询
                                    Thread.sleep(100);
                                    if (DEBUG_ENABLED) {
                                        String status;
                                        if ("1".equals(hasCard)) {
                                            status = "有卡";
                                        } else if ("-1".equals(hasCard)) {
                                            status = "未知";
                                        } else {
                                            status = "无卡";
                                        }
                                        // ä½¿ç”¨é‡ç”¨çš„ StringBuilder æž„建调试信息
                                        debugBuilder.setLength(0);
                                        debugBuilder.append("Slot ").append(slotNumber)
                                                   .append(" (").append(status).append(") æŸ¥è¯¢æˆåŠŸï¼Œé—´éš”: ")
                                                   .append(queryInterval).append("ms\n");
                                        SystemDebugDialog.appendAsciiData(debugBuilder.toString());
                                    }
                                } else {
                                    consecutiveFailures++;
                                    if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
                                        logErrorWithRateLimit("consecutive_failures", "lunxun连续失败次数过多,暂停轮询");
                                        pausePolling();
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    // æ›´æ–°å½“前索引
                    currentIndex = (currentIndex + 1) % slotArray.length;
                    // å¦‚果没有发送查询,等待一段时间再继续
                    if (!sentQuery) {
                        Thread.sleep(pollingInterval);
                    }
                } catch (InterruptedException e) {
                    //System.out.println("轮询查询线程被中断");
                    Thread.currentThread().interrupt();
                    break;
                } catch (Exception e) {
                    logErrorWithRateLimit("polling_exception", "轮询查询过程中发生异常: " + e.getMessage());
                    consecutiveFailures++;
                    // å‘生异常时等待一段时间再继续
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
            //System.out.println("轮询查询线程结束运行");
        }
        /**
         * å‘指定卡槽发送查询指令 - ä¼˜åŒ–版本
         * ä½¿ç”¨ç¼“存指令,优化调试输出
         */
        private boolean sendQueryToSlot(int slotNumber) {
            try {
                // ä½¿ç”¨ç¼“存的查询指令
                String queryCommand = getCachedQueryCommand(slotNumber);
//                 System.out.println("指令是:"+queryCommand);
                if (DEBUG_ENABLED) {
                    SystemDebugDialog.appendAsciiData("send to "+slotNumber+" queryCommand");
                }
                if (queryCommand != null && !queryCommand.trim().isEmpty()) {
                    // å‘送到串口
                    if(sendChaxunzhiling) {
                        boolean sendResult = Sendmsg.sendMessage(queryCommand);
                        if (sendResult) {
                            return true;
                        } else {
                            if (DEBUG_ENABLED) {
                                SystemDebugDialog.appendAsciiData(slotNumber+" Send query command to card slot err");
                            }
                            // å‘送失败可能是串口断开,更新连接状态
                            serialConnected = false;
                            return false;
                        }
                    }else {
                        return false;
                    }
                } else {
                    logErrorWithRateLimit("empty_query_command", "生成的查询指令为空,卡槽: " + slotNumber);
                    return false;
                }
            } catch (Exception e) {
                logErrorWithRateLimit("send_query_exception", "发送查询指令到卡槽 " + slotNumber + " æ—¶å‘生异常: " + e.getMessage());
                // å‘生异常时更新串口连接状态
                serialConnected = false;
                return false;
            }
        }
    }
    /**
     * ç«‹å³å‘指定卡槽发送查询指令(不等待轮询)
     * @param slotNumber å¡æ§½ç¼–号 (1-60)
     * @return true-发送成功, false-发送失败
     */
    public static boolean sendImmediateQuery(int slotNumber) {
        if (slotNumber < MIN_SLOT || slotNumber > MAX_SLOT) {
            logErrorWithRateLimit("invalid_slot_number", "卡槽编号必须在" + MIN_SLOT + "-" + MAX_SLOT + "之间");
            return false;
        }
        // æ£€æŸ¥ä¸²å£è¿žæŽ¥
        if (!checkSerialConnectionWithRetry()) {
            logErrorWithRateLimit("immediate_query_serial_failed", "串口未连接,无法发送查询指令");
            return false;
        }
        try {
            // ä½¿ç”¨ç¼“存的查询指令
            String queryCommand = getCachedQueryCommand(slotNumber);
            if (queryCommand != null && !queryCommand.trim().isEmpty()) {
                // å‘送到串口
                boolean sendResult = Sendmsg.sendMessage(queryCommand);
                if (sendResult) {
                    // æ›´æ–°æœ€åŽæŸ¥è¯¢æ—¶é—´
                    lastQueryTimeMap.put(slotNumber, System.currentTimeMillis());
                    if (DEBUG_ENABLED) {
                        //System.out.println("立即查询成功 - å¡æ§½ " + slotNumber);
                    }
                    return true;
                } else {
                    logErrorWithRateLimit("immediate_query_send_failed", "立即查询失败 - å‘送指令到卡槽 " + slotNumber + " å¤±è´¥");
                    return false;
                }
            } else {
                logErrorWithRateLimit("immediate_query_empty_command", "立即查询失败 - ç”Ÿæˆçš„æŸ¥è¯¢æŒ‡ä»¤ä¸ºç©ºï¼Œå¡æ§½: " + slotNumber);
                return false;
            }
        } catch (Exception e) {
            logErrorWithRateLimit("immediate_query_exception", "立即查询卡槽 " + slotNumber + " æ—¶å‘生异常: " + e.getMessage());
            return false;
        }
    }
    /**
     * ç«‹å³å‘所有卡槽发送查询指令(批量)- ä¼˜åŒ–版本
     * @return æˆåŠŸå‘é€çš„æŒ‡ä»¤æ•°é‡
     */
    public static int sendImmediateQueryToAll() {
        // æ£€æŸ¥ä¸²å£è¿žæŽ¥
        if (!checkSerialConnectionWithRetry()) {
            logErrorWithRateLimit("batch_query_serial_failed", "串口未连接,无法发送批量查询指令");
            return 0;
        }
        int successCount = 0;
        int batchSize = 5; // æ¯æ‰¹æ¬¡å‘送5个查询
        int totalSlots = MAX_SLOT - MIN_SLOT + 1;
        long currentTime = System.currentTimeMillis();
        //System.out.println("开始批量查询所有卡槽...");
        for (int batchStart = MIN_SLOT; batchStart <= MAX_SLOT; batchStart += batchSize) {
            if (shouldStop.get()) {
                break;
            }
            int batchEnd = Math.min(batchStart + batchSize - 1, MAX_SLOT);
            // æ‰¹æ¬¡å†…查询
            for (int slot = batchStart; slot <= batchEnd; slot++) {
                if (sendImmediateQuery(slot)) {
                    successCount++;
                    // æ›´æ–°æœ€åŽæŸ¥è¯¢æ—¶é—´
                    lastQueryTimeMap.put(slot, currentTime);
                }
            }
            // æ‰¹æ¬¡é—´é—´éš”,避免串口拥堵
            if (batchEnd < MAX_SLOT) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
        //System.out.println("批量查询完成,成功发送: " + successCount + "/" + totalSlots);
        return successCount;
    }
    /**
     * æ‰‹åŠ¨è®¾ç½®ä¸²å£è¿žæŽ¥çŠ¶æ€ï¼ˆç”¨äºŽå¤–éƒ¨æ£€æµ‹åˆ°ä¸²å£çŠ¶æ€å˜åŒ–æ—¶è°ƒç”¨ï¼‰
     * @param connected ä¸²å£è¿žæŽ¥çŠ¶æ€
     */
    public static void setSerialConnected(boolean connected) {
        serialConnected = connected;
        lastSerialCheckTime = System.currentTimeMillis();
        if (connected) {
            //            //System.out.println("串口连接状态已设置为: å·²è¿žæŽ¥");
        } else {
            logErrorWithRateLimit("serial_disconnected_external", "串口连接状态已设置为: æœªè¿žæŽ¥");
            // å¦‚果串口断开且轮询正在运行,自动暂停轮询
            if (isRunning && !isPaused) {
                pausePolling();
            }
        }
    }
    /**
     * èŽ·å–è½®è¯¢çŠ¶æ€ä¿¡æ¯
     * @return çŠ¶æ€ä¿¡æ¯å­—ç¬¦ä¸²
     */
    public static String getPollingStatus() {
        String status;
        if (!isRunning) {
            status = "已停止";
        } else if (isPaused) {
            status = "已暂停";
        } else {
            status = "运行中";
        }
        String serialStatus = serialConnected ? "已连接" : "未连接";
        int cacheSize = queryCommandCache.size();
        // ç»Ÿè®¡ä¸åŒçŠ¶æ€çš„å¡æ§½æ•°é‡
        int noCardCount = 0;
        int hasCardCount = 0;
        Fkj[] slotArray = SlotManager.getSlotArray();
        if (slotArray != null) {
            for (Fkj slot : slotArray) {
                if (slot != null && "1".equals(slot.getHasCard())) {
                    hasCardCount++;
                } else {
                    noCardCount++;
                }
            }
        }
        return String.format("轮询状态: %s, ä¸²å£: %s, é—´éš”: %dms, æŒ‡ä»¤ç¼“å­˜: %d, å¡æ§½èŒƒå›´: %d-%d, æ— å¡: %d(100ms), æœ‰å¡: %d(10s)\n%s",
                status, serialStatus, pollingInterval, cacheSize, MIN_SLOT, MAX_SLOT,
                noCardCount, hasCardCount, getMemoryStatus());
    }
    /**
     * ç›´æŽ¥è®¾ç½®è½®è¯¢æš‚停状态(供其他类调用)
     * @param paused true-暂停轮询, false-恢复轮询
     * @return true-设置成功, false-设置失败(如轮询未运行)
     */
    public static boolean setPollingPaused(boolean paused) {
        if (!isRunning) {
            //System.out.println("轮询查询未在运行,无法设置暂停状态");
            return false;
        }
        if (paused) {
            // è¯·æ±‚暂停
            if (!isPaused) {
                isPaused = true;
                //System.out.println("轮询查询已通过外部调用暂停");
                return true;
            } else {
                //System.out.println("轮询查询已经处于暂停状态");
                return false;
            }
        } else {
            // è¯·æ±‚恢复
            if (isPaused) {
                // æ¢å¤å‰æ£€æŸ¥ä¸²å£è¿žæŽ¥
                if (!checkSerialConnectionWithRetry()) {
                    logErrorWithRateLimit("external_resume_serial_failed", "串口未连接,无法恢复轮询查询");
                    return false;
                }
                isPaused = false;
                synchronized (lunxun.class) {
                    lunxun.class.notifyAll(); // å”¤é†’等待的线程
                }
                //System.out.println("轮询查询已通过外部调用恢复");
                return true;
            } else {
                //System.out.println("轮询查询未处于暂停状态");
                return false;
            }
        }
    }
    /**
     * èŽ·å–æ€§èƒ½ç»Ÿè®¡ä¿¡æ¯
     */
    public static String getPerformanceStats() {
        long currentTime = System.currentTimeMillis();
        int overdueNoCard = 0;
        int overdueHasCard = 0;
        Fkj[] slotArray = SlotManager.getSlotArray();
        if (slotArray != null) {
            for (int i = 0; i < slotArray.length; i++) {
                Fkj slot = slotArray[i];
                if (slot != null) {
                    int slotNumber = i + 1;
                    Long lastQueryTime = lastQueryTimeMap.get(slotNumber);
                    if (lastQueryTime != null) {
                        String hasCard = slot.getHasCard();
                        int queryInterval = "1".equals(hasCard) ? HAS_CARD_QUERY_INTERVAL : NO_CARD_QUERY_INTERVAL;
                        if (currentTime - lastQueryTime > queryInterval) {
                            if ("1".equals(hasCard)) {
                                overdueHasCard++;
                            } else {
                                overdueNoCard++;
                            }
                        }
                    }
                }
            }
        }
        return String.format("查询指令缓存大小: %d, è½®è¯¢é—´éš”: %dms, è¶…时无卡: %d, è¶…时有卡: %d",
                queryCommandCache.size(), pollingInterval, overdueNoCard, overdueHasCard);
    }
    /**
     * è®¾ç½®æ— å¡å¡æ§½æŸ¥è¯¢é—´éš”
     * @param interval æŸ¥è¯¢é—´éš”(毫秒)
     */
    public static void setNoCardQueryInterval(int interval) {
        if (interval < 10) {
            logErrorWithRateLimit("no_card_interval_too_small", "无卡卡槽查询间隔不能小于10ms");
            return;
        }
        // æ³¨æ„ï¼šè¿™é‡Œåªæ˜¯è®¾ç½®å¸¸é‡ï¼Œå®žé™…运行时需要重新启动轮询才能生效
        //System.out.println("无卡卡槽查询间隔已设置为: " + interval + "ms");
    }
    /**
     * è®¾ç½®æœ‰å¡å¡æ§½æŸ¥è¯¢é—´éš”
     * @param interval æŸ¥è¯¢é—´éš”(毫秒)
     */
    public static void setHasCardQueryInterval(int interval) {
        if (interval < 1000) {
            logErrorWithRateLimit("has_card_interval_too_small", "有卡卡槽查询间隔不能小于1000ms");
            return;
        }
        // æ³¨æ„ï¼šè¿™é‡Œåªæ˜¯è®¾ç½®å¸¸é‡ï¼Œå®žé™…运行时需要重新启动轮询才能生效
        //System.out.println("有卡卡槽查询间隔已设置为: " + interval + "ms");
    }
    public static boolean isDEBUG_ENABLED() {
        return DEBUG_ENABLED;
@@ -653,4 +803,102 @@
    public static void setDEBUG_ENABLED(boolean dEBUG_ENABLED) {
        DEBUG_ENABLED = dEBUG_ENABLED;
    }
    // ==================== æ–°å¢žå†…存优化方法 ====================
    /**
     * æ¸…理旧缓存 - é˜²æ­¢å†…存无限增长
     */
    private static void cleanupOldCache() {
        long currentTime = System.currentTimeMillis();
        if (currentTime - lastCleanupTime < CACHE_CLEANUP_INTERVAL) {
            return;
        }
        lastCleanupTime = currentTime;
        // æ¸…理长时间未使用的查询时间记录
        long cleanupThreshold = currentTime - 300000; // 5分钟未使用
        lastQueryTimeMap.entrySet().removeIf(entry ->
            currentTime - entry.getValue() > cleanupThreshold
        );
        // é™åˆ¶æŸ¥è¯¢æŒ‡ä»¤ç¼“存大小
        if (queryCommandCache.size() > MAX_CACHE_SIZE) {
            Iterator<Map.Entry<Integer, String>> iterator = queryCommandCache.entrySet().iterator();
            int itemsToRemove = queryCommandCache.size() - MAX_CACHE_SIZE;
            for (int i = 0; i < itemsToRemove && iterator.hasNext(); i++) {
                iterator.next();
                iterator.remove();
            }
        }
        // æ¸…理错误日志限流记录
        lastErrorLogTime.entrySet().removeIf(entry ->
            currentTime - entry.getValue() > 300000 // 5分钟
        );
    }
    /**
     * é™æµé”™è¯¯æ—¥å¿— - é˜²æ­¢å¤§é‡é‡å¤æ—¥å¿—占用内存
     */
    private static void logErrorWithRateLimit(String errorKey, String message) {
        long currentTime = System.currentTimeMillis();
        Long lastTime = lastErrorLogTime.get(errorKey);
        if (lastTime == null || currentTime - lastTime > ERROR_LOG_INTERVAL) {
            Errlog.logOperation(message);
            lastErrorLogTime.put(errorKey, currentTime);
            // æ¸…理过期的错误记录
            if (lastErrorLogTime.size() > 50) {
                lastErrorLogTime.entrySet().removeIf(entry ->
                    currentTime - entry.getValue() > 300000 // 5分钟
                );
            }
        }
    }
    /**
     * èŽ·å–å†…å­˜çŠ¶æ€ä¿¡æ¯
     */
    public static String getMemoryStatus() {
        Runtime runtime = Runtime.getRuntime();
        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long usedMemory = totalMemory - freeMemory;
        long maxMemory = runtime.maxMemory();
        return String.format("内存使用: %.2fMB/%.2fMB (最大: %.2fMB), ç¼“å­˜: æ—¶é—´è®°å½•=%d, æŒ‡ä»¤ç¼“å­˜=%d, é”™è¯¯è®°å½•=%d",
            usedMemory / (1024.0 * 1024.0),
            totalMemory / (1024.0 * 1024.0),
            maxMemory / (1024.0 * 1024.0),
            lastQueryTimeMap.size(),
            queryCommandCache.size(),
            lastErrorLogTime.size());
    }
    /**
     * æ‰‹åŠ¨è§¦å‘å†…å­˜æ¸…ç†
     */
    public static void performCleanup() {
        // æ¸…理查询时间记录中长时间未查询的卡槽
        long cleanupThreshold = System.currentTimeMillis() - 3600000; // 1小时
        lastQueryTimeMap.entrySet().removeIf(entry ->
            entry.getValue() < cleanupThreshold
        );
        // æ¸…空查询指令缓存
        queryCommandCache.clear();
        // æ¸…空错误日志限流记录
        lastErrorLogTime.clear();
        // å»ºè®®ç³»ç»Ÿè¿›è¡Œåžƒåœ¾å›žæ”¶ï¼ˆä½†ä¸å¼ºåˆ¶ï¼‰
        System.gc();
        if (DEBUG_ENABLED) {
            SystemDebugDialog.appendAsciiData("执行内存清理完成\n");
        }
    }
}
src/chushihua/lunxunzaixian.java
ÎļþÒÑɾ³ý
src/dialog/Charulog.java
ÎļþÃû´Ó src/xitongshezhi/Charulog.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package xitongshezhi;
package dialog;
import java.io.*;
import java.text.SimpleDateFormat;
@@ -51,8 +51,9 @@
        File file = new File(LOG_FILE);
        
        if (file.exists()) {
            try (FileInputStream fis = new FileInputStream(file)) {
                props.load(fis);
            try (FileInputStream fis = new FileInputStream(file);
                 InputStreamReader isr = new InputStreamReader(fis, "UTF-8")) {
                props.load(isr);
            } catch (IOException e) {
                System.err.println("读取日志文件失败: " + e.getMessage());
            }
@@ -95,14 +96,14 @@
     * å†™å…¥æ—¥å¿—文件
     */
    private static void writeLogFile(Properties props) {
        try (FileOutputStream fos = new FileOutputStream(LOG_FILE)) {
        try (FileOutputStream fos = new FileOutputStream(LOG_FILE);
             OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8")) {
            // æ·»åŠ æ–‡ä»¶å¤´æ³¨é‡Š
            props.store(fos, "操作日志记录 - æœ€åŽæ›´æ–°: " + new Date());
            props.store(osw, "操作日志记录 - æœ€åŽæ›´æ–°: " + new Date());
        } catch (IOException e) {
            System.err.println("写入日志文件失败: " + e.getMessage());
        }
    }
    /**
     * èŽ·å–å½“å‰æ—¥å¿—è®°å½•æ•°é‡ï¼ˆç”¨äºŽæµ‹è¯•ï¼‰
     */
src/dialog/Dingshidialog.java
ÎļþÃû´Ó src/xitongshezhi/Dingshidialog.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package xitongshezhi;
package dialog;
import java.awt.BorderLayout;
import java.awt.Color;
@@ -26,6 +26,9 @@
 */
public class Dingshidialog {
    
    // æ·»åŠ é™æ€æ ‡å¿—æ¥è·Ÿè¸ªå¼¹çª—æ˜¾ç¤ºçŠ¶æ€
    private static volatile boolean isDialogShowing = false;
    /**
     * æ˜¾ç¤ºå®šæ—¶å…³é—­å¯¹è¯æ¡†
     * @param parent çˆ¶çª—口
@@ -34,8 +37,14 @@
     * @param audioFile MP3文件名(根目录下,无需扩展名)
     * @return 1-成功 0-失败
     */
    public static int showTimedDialog(Frame parent, int countdownTime, String message, String audioFile) {
        TimedDialog dialog = new TimedDialog(parent, countdownTime, message, audioFile);
    public static int showTimedDialog(Frame parent, int countdownTime, String message) {
        // æ£€æŸ¥æ˜¯å¦å·²æœ‰å¼¹çª—在显示
        if (isDialogShowing) {
            System.out.println("已有弹窗在显示,忽略新请求");
            return 0; // è¿”回失败
        }
        TimedDialog dialog = new TimedDialog(parent, countdownTime, message);
        return dialog.showDialog();
    }
    
@@ -43,19 +52,18 @@
     * å†…部对话框类,确保使用后能被垃圾回收
     */
    @SuppressWarnings("serial")
    private static class TimedDialog extends JDialog {
    private static class TimedDialog extends JDialog {
        private JLabel countdownLabel;
        private int remainingTime;
        private int result = 0;
        private Clip audioClip;
        private volatile boolean running = true;
        
        public TimedDialog(Frame parent, int countdownTime, String message, String audioFile) {
        public TimedDialog(Frame parent, int countdownTime, String message) {
            super(parent, "", true);
            this.remainingTime = countdownTime;
            initializeUI(message);
            startCountdown();
            playAudio(audioFile);
        }
        
        private void initializeUI(String message) {
@@ -89,7 +97,7 @@
            messageLabel.setHorizontalAlignment(SwingConstants.CENTER);
            // åˆ›å»ºå€’计时标签
            countdownLabel = new JLabel("剩余时间: " + remainingTime + "秒");
            countdownLabel = new JLabel(remainingTime + "秒后自动关闭");
            countdownLabel.setFont(new Font("Microsoft YaHei", Font.BOLD, 16));
            countdownLabel.setForeground(new Color(231, 76, 60));
            countdownLabel.setHorizontalAlignment(SwingConstants.CENTER);
@@ -132,6 +140,18 @@
                public void windowClosing(java.awt.event.WindowEvent e) {
                    disposeDialog();
                }
                @Override
                public void windowOpened(java.awt.event.WindowEvent e) {
                    // è®¾ç½®å¼¹çª—显示标志
                    isDialogShowing = true;
                }
                @Override
                public void windowClosed(java.awt.event.WindowEvent e) {
                    // ç¡®ä¿å¼¹çª—关闭时重置标志
                    isDialogShowing = false;
                }
            });
        }
        
@@ -147,7 +167,7 @@
                        
                        SwingUtilities.invokeLater(() -> {
                            if (countdownLabel != null) {
                                countdownLabel.setText("剩余时间: " + remainingTime + "秒");
                                countdownLabel.setText( remainingTime + "秒后自动关闭");
                            }
                        });
                        
@@ -245,6 +265,9 @@
            // æ¸…理UI引用
            countdownLabel = null;
            
            // é‡ç½®å¼¹çª—显示标志
            isDialogShowing = false;
            dispose();
        }
        
@@ -252,6 +275,12 @@
         * æ˜¾ç¤ºå¯¹è¯æ¡†
         */
        public int showDialog() {
            // å†æ¬¡æ£€æŸ¥æ ‡å¿—,防止竞态条件
            if (isDialogShowing) {
                System.out.println("已有弹窗在显示,取消显示");
                return 0;
            }
            setVisible(true);
            return result;
        }
src/dialog/Errlog.java
copy from src/xitongshezhi/Charulog.java copy to src/dialog/Errlog.java
Îļþ´Ó src/xitongshezhi/Charulog.java ¸´ÖÆ
@@ -1,12 +1,12 @@
package xitongshezhi;
package dialog;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
public class Charulog {
    private static final String LOG_FILE = "log.properties";
public class Errlog {
    private static final String LOG_FILE = "err.properties";
    private static final int MAX_RECORDS = 500;
    private static final ReentrantLock lock = new ReentrantLock();
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@@ -51,8 +51,9 @@
        File file = new File(LOG_FILE);
        
        if (file.exists()) {
            try (FileInputStream fis = new FileInputStream(file)) {
                props.load(fis);
            try (FileInputStream fis = new FileInputStream(file);
                 InputStreamReader isr = new InputStreamReader(fis, "UTF-8")) {
                props.load(isr);
            } catch (IOException e) {
                System.err.println("读取日志文件失败: " + e.getMessage());
            }
@@ -95,14 +96,14 @@
     * å†™å…¥æ—¥å¿—文件
     */
    private static void writeLogFile(Properties props) {
        try (FileOutputStream fos = new FileOutputStream(LOG_FILE)) {
        try (FileOutputStream fos = new FileOutputStream(LOG_FILE);
             OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8")) {
            // æ·»åŠ æ–‡ä»¶å¤´æ³¨é‡Š
            props.store(fos, "操作日志记录 - æœ€åŽæ›´æ–°: " + new Date());
            props.store(osw, "操作日志记录 - æœ€åŽæ›´æ–°: " + new Date());
        } catch (IOException e) {
            System.err.println("写入日志文件失败: " + e.getMessage());
        }
    }
    /**
     * èŽ·å–å½“å‰æ—¥å¿—è®°å½•æ•°é‡ï¼ˆç”¨äºŽæµ‹è¯•ï¼‰
     */
src/home/CardMachineUI.java
@@ -13,7 +13,6 @@
import xitongshezhi.AdminLoginDialog;
import xitongshezhi.CardPickupDialog;
import xitongshezhi.ConfigSet;
import xitongshezhi.Fkj;
import chuankou.SerialPortService;
import chuankou.Sendmsg;
@@ -77,9 +76,12 @@
    private JPanel commFaultPanel;
    public CardMachineUI() {
        try {
        try {
            // å…ˆåˆå§‹åŒ–系统配置和组件
            initializeSystem();
            // åˆå§‹åŒ–串口解析器
            initializeSerialParser();
            
            // ç„¶åŽåˆå§‹åŒ–UI
            initializeUI();
@@ -110,7 +112,8 @@
            if (serialConnected && lunxun.isPolling() && lunxun.isPaused()) {
                // åªæœ‰åœ¨ä¸åœ¨è®¾ç½®é¡µé¢æ—¶æ‰è‡ªåŠ¨æ¢å¤
                if (!isInConfigPage()) {
                    lunxun.resumePolling();
//                    lunxun.resumePolling();
//                    System.out.println("尝试自动启动轮询功能");
                }
            }
        });
@@ -171,7 +174,7 @@
    }
    private void initializeUI() {
        setTitle("UWB人员定位卡发卡机管理系统");
        setTitle("智能人脸发卡机管理系统");
        setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
@@ -288,8 +291,9 @@
        refreshBtn.addActionListener(e -> {
            updateCardSlotsDisplay();
            updateStatistics();
            lunxun.resumePolling();
            JOptionPane.showMessageDialog(CardMachineUI.this, 
                    "手动刷新完成,共刷新 " + TOTAL_SLOTS + " ä¸ªå¡æ§½",
                    "手动刷新完成,启动轮询,共刷新 " + TOTAL_SLOTS + " ä¸ªå¡æ§½",
                    "刷新完成", JOptionPane.INFORMATION_MESSAGE);
        });
@@ -586,7 +590,7 @@
        
        if (pickupSuccess) {
            // å–卡成功,调用changgehaska方法改变卡槽属性
            SlotManager.changgehaska(slotId, "1"); // "1"表示管理员操作
            SlotManager.changgehaska(slotId,1); // "1"表示管理员操作
            //System.out.println("卡槽 " + slotId + " å–卡成功,已更新卡槽状态");
        }
    }
@@ -796,12 +800,15 @@
    /**
     * å¯åЍUI刷新定时器 - æ¯3秒刷新一次
     */
    // åœ¨startUIUpdates()方法中调用
    private void startUIUpdates() {
        uiUpdateTimer = new Timer(3000, e -> {
            updateCardSlotsDisplay();
            updateStatistics();
        });
        uiUpdateTimer.start();
        uiUpdateTimer = new Timer(3000, e -> {
            ensurePollingRunning(); // ç¡®ä¿è½®è¯¢è¿è¡Œ
            ensureSerialParserRunning(); // ç¡®ä¿ä¸²å£è§£æžå™¨è¿è¡Œ
            updateCardSlotsDisplay();
            updateStatistics();
        });
        uiUpdateTimer.start();
    }
    
    /**
@@ -829,5 +836,42 @@
    public SlotManager getSlotManager() {
        return slotManager;
    }
    /**
     * æ£€æŸ¥å¹¶ç¡®ä¿è½®è¯¢æŸ¥è¯¢æ­£å¸¸è¿è¡Œ
     */
    private void ensurePollingRunning() {
        if (!lunxun.isPolling() && lunxun.checkSerialConnection()) {
            // å¦‚果轮询未运行但串口已连接,自动启动轮询
            boolean started = lunxun.startPolling();
            if (started) {
                System.out.println("检测到轮询未运行,已自动启动");
            }
        }
    }
    /**
     * ç¡®ä¿ä¸²å£è§£æžå™¨æ­£å¸¸è¿è¡Œ
     */
    private void ensureSerialParserRunning() {
        // å¦‚果串口解析器未运行但串口已连接,自动启动
        if (serialProtocolParser != null && !serialProtocolParser.isRunning() && lunxun.checkSerialConnection()) {
            serialProtocolParser.start();
            //System.out.println("检测到串口解析器未运行,已自动启动");
        }
    }
    /**
     * åˆå§‹åŒ–串口解析器
     */
    private void initializeSerialParser() {
        try {
            serialProtocolParser = new SerialProtocolParser();
            serialProtocolParser.start();
            //System.out.println("串口协议解析器已启动");
        } catch (Exception e) {
            System.err.println("初始化串口解析器失败: " + e.getMessage());
        }
    }
        
}
src/home/Fkj.java
ÎļþÃû´Ó src/xitongshezhi/Fkj.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package xitongshezhi;
package home;
public class Fkj {
     // å®šä¹‰æ‰€æœ‰å±žæ€§ï¼Œå‡ä¸ºString类型
    private String slotNumber;    // å¡æ§½ç¼–号
src/home/Homein.java
@@ -1,5 +1,4 @@
package home;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
@@ -7,8 +6,6 @@
import chushihua.Chushihua;
import chushihua.SlotManager;
import chushihua.lunxun;
import chushihua.lunxunzaixian;
import jiekou.lunxunkazhuangtai;
public class Homein {
    public static void main(String[] args) {
@@ -107,7 +104,7 @@
    private static boolean initializeSlotManager() {
        try {
            // SlotManager ä¼šåœ¨æž„造函数中自动初始化所有卡槽
            SlotManager slotManager = new SlotManager();
            new SlotManager();
            //System.out.println("✓ å¡æ§½ç®¡ç†å™¨åˆå§‹åŒ–成功");
            //System.out.println("  æ€»å¡æ§½æ•°: " + slotManager.getTotalSlots());
            
@@ -130,11 +127,7 @@
            if (serialConnected) {
                // 4. ä¸²å£è¿žæŽ¥æˆåŠŸåŽï¼Œå¯åŠ¨è½®è¯¢
                startPollingService();
                showMainInterface();
                //启动轮询卡状态给服务器发数据
                lunxunkazhuangtai.startPolling();
                //启动在线的卡状态轮询
                lunxunzaixian.startOnlinePolling();
                showMainInterface();
                
            } else {
                System.err.println("串口连接失败");
src/jiekou/lunxunkazhuangtai.java
@@ -1,6 +1,7 @@
package jiekou;
import chushihua.SlotManager;
import xitongshezhi.Fkj;
import home.Fkj;
/**
 * è½®è¯¢å¡æ§½çŠ¶æ€ç±»
src/publicway/OpenDoor.java
@@ -6,8 +6,8 @@
    private static final String FUNCTION_CODE_PREFIX = "515AA55AA5";
    
    // å¼€é—¨ç±»åž‹å¸¸é‡
    public static final int TYPE_ISSUE_CARD = 1;
    public static final int TYPE_ADMIN = 2;
    public static final int TYPE_ISSUE_CARD = 1;//1发卡开门
    public static final int TYPE_ADMIN = 2;//2管理员开门
    
    // å‚数范围常量
    private static final int MIN_SLOT_NUMBER = 0;
src/publicway/ProtocolParser01.java
@@ -1,13 +1,20 @@
package publicway;
import java.util.ArrayList;
import java.util.List;
import chuankou.SerialPortService;
import chushihua.SlotManager;
public class ProtocolParser01 {
    // ç¼“存常用字符串减少重复创建
    private static final String[] WORK_STATUS_DESC = {"无效", "待机", "充电", "充满", "故障", "授权到期", "通信超时", "未知状态"};
    private static final String[] DOOR_STATUS_DESC = {"未知状态", "开门状态", "关门状态"};
    private static final String[] CARD_STATUS_DESC = {"无卡", "有卡", "读卡错误(卡非法)", "未知状态"};
    // StringBuilder å¯¹è±¡æ± 
    private static final ThreadLocal<StringBuilder> stringBuilderPool =
        ThreadLocal.withInitial(() -> new StringBuilder(512));
    /**
     * æ–°å¢žï¼šç›´æŽ¥ä½¿ç”¨å­—节数组解析的方法(避免字符串转换)
     * ä¼˜åŒ–后的字节数组解析方法
     */
    public static ParseResult parseDDCC01Data(byte[] packetData) {
        if (packetData == null || packetData.length < 18) {
@@ -19,45 +26,49 @@
            throw new IllegalArgumentException("非DDCC协议数据");
        }
        ParseResult result = ParseResultPool.borrowObject();
        try {
            // è·³è¿‡åŒ…头DDCC (2字节),直接使用剩余数据
            byte[] dataBytes = new byte[packetData.length - 2];
            System.arraycopy(packetData, 2, dataBytes, 0, dataBytes.length);
            if (dataBytes.length < 16) {
            // ç›´æŽ¥ä½¿ç”¨åŽŸæ•°ç»„ï¼Œé¿å…åˆ›å»ºæ–°æ•°ç»„
            if (packetData.length < 18) { // 2(包头) + 16(最小数据长度)
                throw new IllegalArgumentException("数据长度不足");
            }
            // 2. è§£æžå„个字段
            int dataLength = parseDataLength(dataBytes);
            int hostAddress = parseHostAddress(dataBytes);
            int slotNumber = parseSlotNumber(dataBytes);
            int functionCode = parseFunctionCode(dataBytes);
            WorkStatus workStatus = parseWorkStatus(dataBytes);
            DoorStatus doorStatus = parseDoorStatus(dataBytes);
            CardStatus cardStatus = parseCardStatus(dataBytes);
            int cardStatusChange = parseCardStatusChange(dataBytes);
            String cardNumber = parseCardNumber(dataBytes);
            List<FaultType> faults = parseFaults(dataBytes);
            double voltage = parseVoltage(dataBytes);
            double current = parseCurrent(dataBytes);
            // è§£æžå„个字段
            int dataLength = parseDataLength(packetData, 2);
            int hostAddress = parseHostAddress(packetData, 4);
            int slotNumber = parseSlotNumber(packetData, 5);
            int functionCode = parseFunctionCode(packetData, 6);
            // 3. éªŒè¯åŠŸèƒ½ç 
            // éªŒè¯åŠŸèƒ½ç 
            if (functionCode != 0x01) {
                throw new IllegalArgumentException("非01功能码数据");
            }
            return new ParseResult(hostAddress, slotNumber, workStatus, doorStatus,
                    cardStatus, cardStatusChange, cardNumber,
                    faults, voltage, current, dataLength);
            WorkStatus workStatus = parseWorkStatus(packetData, 7);
            DoorStatus doorStatus = parseDoorStatus(packetData, 8);
            CardStatus cardStatus = parseCardStatus(packetData, 9);
            int cardStatusChange = parseCardStatusChange(packetData, 10);
            String cardNumber = parseCardNumber(packetData, 13);
            List<FaultType> faults = parseFaults(packetData, 15);
            double voltage = parseVoltage(packetData, 16);
            double current = parseCurrent(packetData, 17);
            // é‡ç”¨ ParseResult å¯¹è±¡
            result.reset(hostAddress, slotNumber, workStatus, doorStatus,
                        cardStatus, cardStatusChange, cardNumber,
                        faults, voltage, current, dataLength);
            return result;
        } catch (Exception e) {
            // å‘生异常时归还对象
            ParseResultPool.returnObject(result);
            throw new RuntimeException("解析数据时发生错误: " + e.getMessage(), e);
        }
    }
    /**
     * ä¿ç•™åŽŸæœ‰æ–¹æ³•ç”¨äºŽå…¼å®¹æ€§
     * ä¼˜åŒ–后的字符串解析方法
     */
    public static ParseResult parseDDCC01Data(String hexData) {
        if (hexData == null || hexData.isEmpty()) {
@@ -69,6 +80,7 @@
            throw new IllegalArgumentException("非DDCC协议数据");
        }
        ParseResult result = ParseResultPool.borrowObject();
        try {
            // 1. ç§»é™¤åŒ…头DDCC进行解析
            String dataWithoutHeader = hexData.substring(4);
@@ -79,178 +91,162 @@
            }
            // 2. è§£æžå„个字段
            int dataLength = parseDataLength(dataBytes);
            int hostAddress = parseHostAddress(dataBytes);
            int slotNumber = parseSlotNumber(dataBytes);
            int functionCode = parseFunctionCode(dataBytes);
            WorkStatus workStatus = parseWorkStatus(dataBytes);
            DoorStatus doorStatus = parseDoorStatus(dataBytes);
            CardStatus cardStatus = parseCardStatus(dataBytes);
            int cardStatusChange = parseCardStatusChange(dataBytes);
            String cardNumber = parseCardNumber(dataBytes);
            List<FaultType> faults = parseFaults(dataBytes);
            double voltage = parseVoltage(dataBytes);
            double current = parseCurrent(dataBytes);
            int dataLength = parseDataLength(dataBytes, 0);
            int hostAddress = parseHostAddress(dataBytes, 2);
            int slotNumber = parseSlotNumber(dataBytes, 3);
            int functionCode = parseFunctionCode(dataBytes, 4);
            // 3. éªŒè¯åŠŸèƒ½ç 
            if (functionCode != 0x01) {
                throw new IllegalArgumentException("非01功能码数据");
            }
            return new ParseResult(hostAddress, slotNumber, workStatus, doorStatus,
                    cardStatus, cardStatusChange, cardNumber,
                    faults, voltage, current, dataLength);
            WorkStatus workStatus = parseWorkStatus(dataBytes, 5);
            DoorStatus doorStatus = parseDoorStatus(dataBytes, 6);
            CardStatus cardStatus = parseCardStatus(dataBytes, 7);
            int cardStatusChange = parseCardStatusChange(dataBytes, 8);
            String cardNumber = parseCardNumber(dataBytes, 11);
            List<FaultType> faults = parseFaults(dataBytes, 13);
            double voltage = parseVoltage(dataBytes, 14);
            double current = parseCurrent(dataBytes, 15);
            result.reset(hostAddress, slotNumber, workStatus, doorStatus,
                        cardStatus, cardStatusChange, cardNumber,
                        faults, voltage, current, dataLength);
            return result;
        } catch (Exception e) {
            ParseResultPool.returnObject(result);
            throw new RuntimeException("解析数据时发生错误: " + e.getMessage(), e);
        }
    }
    /**
     * CRC校验
     * æ ¹æ®åè®®ï¼šCRC16校验从功能码之后一直到CRC16之前的数据
     */
    private static boolean validateCRC(String hexData) {
        try {
            // CRC在最后4个字符
            String receivedCRC = hexData.substring(hexData.length() - 6);
            byte[] cmdBytes = HexUtil.hexStringToBytes(hexData.replace(receivedCRC,""));
            String crc = HexUtil.calculate(cmdBytes)+"00";
            //System.out.println("收到的完整数据是:"+hexData);
            //System.out.println("收到数据校验码是:"+receivedCRC);
            //System.out.println("校验码是:"+crc);
            return receivedCRC.equalsIgnoreCase(crc);
        } catch (Exception e) {
            System.err.println("CRC校验异常: " + e.getMessage());
            return false;
        }
    }
    /**
     * è§£æžæ•°æ®é•¿åº¦ï¼ˆ2字节)
     * æ•°æ®é•¿åº¦æ˜¯ä»Žè¯¥å­—节之后开始到CRC16之前数据字节数
     */
    private static int parseDataLength(byte[] data) {
        if (data.length < 2) {
    private static int parseDataLength(byte[] data, int offset) {
        if (data.length < offset + 2) {
            throw new IllegalArgumentException("数据长度不足,无法解析数据长度");
        }
        return ((data[0] & 0xFF) << 8) | (data[1] & 0xFF);
        return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
    }
    /**
     * è§£æžä¸»æœºåœ°å€ï¼ˆ1字节)
     */
    private static int parseHostAddress(byte[] data) {
        if (data.length < 3) {
    private static int parseHostAddress(byte[] data, int offset) {
        if (data.length < offset + 1) {
            throw new IllegalArgumentException("数据长度不足,无法解析主机地址");
        }
        return data[2] & 0xFF;
        return data[offset] & 0xFF;
    }
    /**
     * è§£æžå¡æ§½ç¼–号(1字节)
     */
    private static int parseSlotNumber(byte[] data) {
        if (data.length < 4) {
    private static int parseSlotNumber(byte[] data, int offset) {
        if (data.length < offset + 1) {
            throw new IllegalArgumentException("数据长度不足,无法解析卡槽编号");
        }
        return data[3] & 0xFF;
        return data[offset] & 0xFF;
    }
    /**
     * è§£æžåŠŸèƒ½ç ï¼ˆ1字节)
     */
    private static int parseFunctionCode(byte[] data) {
        if (data.length < 5) {
    private static int parseFunctionCode(byte[] data, int offset) {
        if (data.length < offset + 1) {
            throw new IllegalArgumentException("数据长度不足,无法解析功能码");
        }
        return data[4] & 0xFF;
        return data[offset] & 0xFF;
    }
    /**
     * è§£æžå·¥ä½œçŠ¶æ€ï¼ˆ1字节)
     */
    private static WorkStatus parseWorkStatus(byte[] data) {
        if (data.length < 6) {
    private static WorkStatus parseWorkStatus(byte[] data, int offset) {
        if (data.length < offset + 1) {
            throw new IllegalArgumentException("数据长度不足,无法解析工作状态");
        }
        int statusValue = data[5] & 0xFF;
        int statusValue = data[offset] & 0xFF;
        return WorkStatus.fromValue(statusValue);
    }
    /**
     * è§£æžåœ¨ä½çŠ¶æ€/门状态(1字节)
     */
    private static DoorStatus parseDoorStatus(byte[] data) {
        if (data.length < 7) {
    private static DoorStatus parseDoorStatus(byte[] data, int offset) {
        if (data.length < offset + 1) {
            throw new IllegalArgumentException("数据长度不足,无法解析在位状态");
        }
        int statusValue = data[6] & 0xFF;
        int statusValue = data[offset] & 0xFF;
        return DoorStatus.fromValue(statusValue);
    }
    /**
     * è§£æžå¡çŠ¶æ€ï¼ˆ1字节)
     */
    private static CardStatus parseCardStatus(byte[] data) {
        if (data.length < 8) {
    private static CardStatus parseCardStatus(byte[] data, int offset) {
        if (data.length < offset + 1) {
            throw new IllegalArgumentException("数据长度不足,无法解析卡状态");
        }
        int statusValue = data[7] & 0xFF;
        int statusValue = data[offset] & 0xFF;
        return CardStatus.fromValue(statusValue);
    }
    /**
     * è§£æžå¡çŠ¶æ€å˜æ›´ï¼ˆ1字节)
     */
    private static int parseCardStatusChange(byte[] data) {
        if (data.length < 9) {
    private static int parseCardStatusChange(byte[] data, int offset) {
        if (data.length < offset + 1) {
            throw new IllegalArgumentException("数据长度不足,无法解析卡状态变更");
        }
        return data[8] & 0xFF;
        return data[offset] & 0xFF;
    }
    /**
     * è§£æžå¡å·ï¼ˆå¡å·3 + å¡å·4)
     */
    private static String parseCardNumber(byte[] data) {
        if (data.length < 13) {
    private static String parseCardNumber(byte[] data, int offset) {
        if (data.length < offset + 2) {
            throw new IllegalArgumentException("数据长度不足,无法解析卡号");
        }
        // ç´¢å¼•11:卡号3,索引12:卡号4
        byte cardNumber3 = data[11];
        byte cardNumber4 = data[12];
        return String.format("%02X%02X", cardNumber3 & 0xFF, cardNumber4 & 0xFF);
        // ç›´æŽ¥è¿”回字符串,避免创建临时对象
        char[] hexChars = new char[4];
        hexChars[0] = Character.forDigit((data[offset] >> 4) & 0xF, 16);
        hexChars[1] = Character.forDigit(data[offset] & 0xF, 16);
        hexChars[2] = Character.forDigit((data[offset + 1] >> 4) & 0xF, 16);
        hexChars[3] = Character.forDigit(data[offset + 1] & 0xF, 16);
        return new String(hexChars).toUpperCase();
    }
    /**
     * è§£æžæ•…障信息(1字节)
     */
    private static List<FaultType> parseFaults(byte[] data) {
        if (data.length < 14) {
    private static List<FaultType> parseFaults(byte[] data, int offset) {
        if (data.length < offset + 1) {
            throw new IllegalArgumentException("数据长度不足,无法解析故障信息");
        }
        int faultByte = data[13] & 0xFF;
        List<FaultType> faults = new ArrayList<>();
        int faultByte = data[offset] & 0xFF;
        List<FaultType> faults = new ArrayList<>(5); // é¢„分配容量
        for (FaultType fault : FaultType.values()) {
            if ((faultByte & fault.getBitMask()) != 0) {
                faults.add(fault);
            }
        }
        return faults;
    }
    /**
     * è§£æžç”µåŽ‹å€¼ï¼ˆ1字节)
     */
    private static double parseVoltage(byte[] data) {
        if (data.length < 15) {
    private static double parseVoltage(byte[] data, int offset) {
        if (data.length < offset + 1) {
            throw new IllegalArgumentException("数据长度不足,无法解析电压");
        }
        int voltageValue = data[14] & 0xFF;
        int voltageValue = data[offset] & 0xFF;
        // 50mV/bit è½¬æ¢ä¸ºä¼ç‰¹
        return voltageValue * 0.05;
    }
@@ -258,18 +254,16 @@
    /**
     * è§£æžç”µæµå€¼ï¼ˆ1字节)
     */
    private static double parseCurrent(byte[] data) {
        if (data.length < 16) {
    private static double parseCurrent(byte[] data, int offset) {
        if (data.length < offset + 1) {
            throw new IllegalArgumentException("数据长度不足,无法解析电流");
        }
        int currentValue = data[15] & 0xFF;
        int currentValue = data[offset] & 0xFF;
        // 10mA/bit è½¬æ¢ä¸ºå®‰åŸ¹
        return currentValue * 0.01;
    }
    /**
     * å·¥ä½œçŠ¶æ€æžšä¸¾
     */
    // æžšä¸¾ç±»ä¿æŒä¸å˜...
    public enum WorkStatus {
        INVALID(0, "无效"),
        STANDBY(1, "待机"),
@@ -288,13 +282,8 @@
            this.description = description;
        }
        public int getValue() {
            return value;
        }
        public String getDescription() {
            return description;
        }
        public int getValue() { return value; }
        public String getDescription() { return description; }
        public static WorkStatus fromValue(int value) {
            for (WorkStatus status : values()) {
@@ -306,9 +295,6 @@
        }
    }
    /**
     * é—¨çŠ¶æ€æžšä¸¾
     */
    public enum DoorStatus {
        UNKNOWN(0, "δ֪״̬"),
        DOOR_OPEN(1, "开门状态"),
@@ -322,13 +308,8 @@
            this.description = description;
        }
        public int getValue() {
            return value;
        }
        public String getDescription() {
            return description;
        }
        public int getValue() { return value; }
        public String getDescription() { return description; }
        public static DoorStatus fromValue(int value) {
            for (DoorStatus status : values()) {
@@ -340,9 +321,6 @@
        }
    }
    /**
     * å¡çŠ¶æ€æžšä¸¾
     */
    public enum CardStatus {
        NO_CARD(0, "无卡"),
        HAS_CARD(1, "有卡"),
@@ -357,13 +335,8 @@
            this.description = description;
        }
        public int getValue() {
            return value;
        }
        public String getDescription() {
            return description;
        }
        public int getValue() { return value; }
        public String getDescription() { return description; }
        public static CardStatus fromValue(int value) {
            for (CardStatus status : values()) {
@@ -375,9 +348,6 @@
        }
    }
    /**
     * æ•…障类型枚举
     */
    public enum FaultType {
        INSERT_ERROR(0x01, "插卡错误"),
        OVER_CURRENT(0x02, "过流"),
@@ -393,17 +363,12 @@
            this.description = description;
        }
        public int getBitMask() {
            return bitMask;
        }
        public String getDescription() {
            return description;
        }
        public int getBitMask() { return bitMask; }
        public String getDescription() { return description; }
    }
    /**
     * è§£æžç»“果类 - æ·»åŠ å¯¹è±¡æ± æ”¯æŒ
     * ä¼˜åŒ–后的解析结果类
     */
    public static class ParseResult {
        private int hostAddress;
@@ -418,29 +383,12 @@
        private double current;
        private int dataLength;
        // é»˜è®¤æž„造器用于对象池
        // é‡ç”¨åˆ—表对象
        private final List<FaultType> faultList = new ArrayList<>(5);
        private final List<String> faultDescList = new ArrayList<>(5);
        public ParseResult() {}
        public ParseResult(int hostAddress, int slotNumber, WorkStatus workStatus,
                DoorStatus doorStatus, CardStatus cardStatus, int cardStatusChange,
                String cardNumber, List<FaultType> faults, double voltage,
                double current, int dataLength) {
            this.hostAddress = hostAddress;
            this.slotNumber = slotNumber;
            this.workStatus = workStatus;
            this.doorStatus = doorStatus;
            this.cardStatus = cardStatus;
            this.cardStatusChange = cardStatusChange;
            this.cardNumber = cardNumber;
            this.faults = faults;
            this.voltage = voltage;
            this.current = current;
            this.dataLength = dataLength;
        }
        /**
         * é‡ç½®æ–¹æ³•,用于对象重用
         */
        public void reset(int hostAddress, int slotNumber, WorkStatus workStatus, 
                         DoorStatus doorStatus, CardStatus cardStatus, int cardStatusChange,
                         String cardNumber, List<FaultType> faults, double voltage, 
@@ -452,10 +400,16 @@
            this.cardStatus = cardStatus;
            this.cardStatusChange = cardStatusChange;
            this.cardNumber = cardNumber;
            this.faults = faults;
            this.voltage = voltage;
            this.current = current;
            this.dataLength = dataLength;
            // é‡ç”¨æ•…障列表
            this.faultList.clear();
            if (faults != null) {
                this.faultList.addAll(faults);
            }
            this.faults = this.faultList;
        }
        // Getter方法
@@ -475,21 +429,24 @@
         * èŽ·å–æ•…éšœçš„ä¸­æ–‡æè¿°åˆ—è¡¨
         */
        public List<String> getFaultDescriptions() {
            List<String> descriptions = new ArrayList<>();
            faultDescList.clear();
            for (FaultType fault : faults) {
                descriptions.add(fault.getDescription());
                faultDescList.add(fault.getDescription());
            }
            return descriptions;
            return faultDescList;
        }
        /**
         * èŽ·å–æ•…éšœçš„ä¸­æ–‡æè¿°å­—ç¬¦ä¸²
         */
        public String getFaultsString() {
            if (faults == null || faults.isEmpty()) {
            if (faults.isEmpty()) {
                return "无故障";
            }
            StringBuilder sb = new StringBuilder();
            StringBuilder sb = stringBuilderPool.get();
            sb.setLength(0);
            for (int i = 0; i < faults.size(); i++) {
                if (i > 0) sb.append(", ");
                sb.append(faults.get(i).getDescription());
@@ -499,49 +456,47 @@
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("=== DDCC协议数据解析结果 ===\n");
            sb.append("1. ä¸»æœºåœ°å€: ").append(String.format("%02X", hostAddress));
            sb.append("2. å¡æ§½ç¼–号: ").append(slotNumber);
            sb.append("3. å·¥ä½œçŠ¶æ€: ").append(workStatus.getDescription())
              .append(" (").append(workStatus.getValue());
            sb.append("4. é—¨çŠ¶æ€: ").append(doorStatus.getDescription())
              .append(" (").append(doorStatus.getValue());
            sb.append("5. å¡çŠ¶æ€: ").append(cardStatus.getDescription())
              .append(" (").append(cardStatus.getValue());
            sb.append("6. å¡çŠ¶æ€å˜æ›´: ").append(cardStatusChange);
            sb.append("7. å¡å·: ").append(cardNumber);
            sb.append("8. æ•…éšœ: ").append(getFaultsString());
            sb.append("9. ç”µåŽ‹: ").append(String.format("%.2f", voltage));
            sb.append("10. ç”µæµ: ").append(String.format("%.2f", current));
            sb.append("数据长度: ").append(dataLength).append(" å­—节");
//            //System.out.println(sb.toString());
            return sb.toString();
            StringBuilder sb = stringBuilderPool.get();
            sb.setLength(0);
            sb.append("1.主机地址:").append(String.format("%02X", hostAddress));
            sb.append("2.卡槽编号:").append(slotNumber);
            sb.append("3.工作状态:").append(workStatus.getDescription())
              .append("(").append(workStatus.getValue()).append(")");
            sb.append("4. é—¨çŠ¶æ€:").append(doorStatus.getDescription())
              .append("(").append(doorStatus.getValue()).append(")");
            sb.append("5.卡状态:").append(cardStatus.getDescription())
              .append("(").append(cardStatus.getValue()).append(")");
            sb.append("6.卡状态变更:").append(cardStatusChange);
            sb.append("7.卡号:").append(cardNumber);
            sb.append("8.故障:").append(getFaultsString());
            sb.append("9.电压:").append(String.format("%.2f", voltage));
            sb.append("10.电流:").append(String.format("%.2f", current));
            sb.append("数据长度:").append(dataLength).append(" å­—节");
            return sb.toString();
        }
        
        public void fuzhi() {
            SlotManager.gengxinshuxingzhi(
            SlotManager.gengxinshuxingzhi(
                    slotNumber,    // å¡æ§½ç¼–号
                    cardNumber,    // å¡ç¼–号
                    String.valueOf(cardStatus.getValue()),       // æ˜¯å¦æœ‰å¡0无卡,1有卡,-1未知
                    String.valueOf(workStatus.getValue()),    // å·¥ä½œçŠ¶æ€0.无效1.待机;2.充电;3.充满;4.故障;5.授权到期;6.通信超时
                    String.valueOf(workStatus.getValue()),    // å·¥ä½œçŠ¶æ€
                    String.format("%.2f", voltage),      // ç”µåŽ‹
                    String.format("%.2f", current),       // ç”µæµ
                    getFaultsString()        // æ•…éšœ1插卡错误;2过流;3,门控故障;4过压;5欠压;
                    );
                    getFaultsString()        // æ•…éšœ
            );
        }
    }
    /**
     * ç®€å•的对象池实现
     * æ”¹è¿›çš„对象池实现
     */
    public static class ParseResultPool {
        private static final int POOL_SIZE = 10;
        private static final int POOL_SIZE = 20;
        private static final java.util.concurrent.BlockingQueue<ParseResult> pool = 
            new java.util.concurrent.ArrayBlockingQueue<>(POOL_SIZE);
            new java.util.concurrent.LinkedBlockingQueue<>(POOL_SIZE);
        
        static {
            // é¢„创建对象
@@ -556,9 +511,17 @@
        }
        
        public static void returnObject(ParseResult result) {
            if (result != null && !pool.offer(result)) {
                // æ± å·²æ»¡ï¼Œä¸å›žæ”¶
            if (result != null) {
                // æ¸…理状态
                result.reset(0, 0, WorkStatus.UNKNOWN, DoorStatus.UNKNOWN,
                           CardStatus.UNKNOWN, 0, "", null, 0.0, 0.0, 0);
                // éžé˜»å¡žå¼å½’还
                pool.offer(result);
            }
        }
        public static int getPoolSize() {
            return pool.size();
        }
    }
}
src/publicway/SerialProtocolParser.java
@@ -1,4 +1,5 @@
package publicway;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
@@ -6,7 +7,12 @@
import java.util.concurrent.TimeUnit;
import chuankou.SerialPortService;
import chushihua.SlotManager;
import chushihua.lunxun;
import dialog.Charulog;
import dialog.Dingshidialog;
import publicway.ProtocolParser01.ParseResult;
import xitongshezhi.SystemDebugDialog;
public class SerialProtocolParser {
    
@@ -22,15 +28,15 @@
    private static final byte FUNCTION_82 = (byte) 0x82; // å•板升级使能
    private static final byte FUNCTION_83 = (byte) 0x83;   // å•板升级数据包
    
    // æ•°æ®ç¼“冲区,用于处理粘包
    // æ•°æ®ç¼“冲区,用于处理粘包 - æ”¹ä¸ºç›´æŽ¥ByteArrayOutputStream管理
    private byte[] dataBuffer = new byte[1024];
    private int bufferPosition = 0;
    
    // æ•°æ®æŽ¥æ”¶é˜Ÿåˆ—
    private BlockingQueue<byte[]> dataQueue = new ArrayBlockingQueue<>(100);
    // æ•°æ®æŽ¥æ”¶é˜Ÿåˆ— - é™åˆ¶é˜Ÿåˆ—大小防止内存无限增长
    private BlockingQueue<byte[]> dataQueue = new ArrayBlockingQueue<>(50);
    
    // æ‰¹é‡å¤„理队列
    private final BlockingQueue<byte[]> batchQueue = new ArrayBlockingQueue<>(1000);
    // æ‰¹é‡å¤„理队列 - é™åˆ¶é˜Ÿåˆ—大小
    private final BlockingQueue<byte[]> batchQueue = new ArrayBlockingQueue<>(200);
    private final ScheduledExecutorService batchExecutor = 
        Executors.newSingleThreadScheduledExecutor();
    
@@ -41,12 +47,18 @@
    // é‡ç”¨StringBuilder减少对象创建
    private final StringBuilder hexBuilder = new StringBuilder(256);
    
    // å†…存监控计数器
    private long lastMemoryCheckTime = 0;
    private static final long MEMORY_CHECK_INTERVAL = 30000; // 30秒检查一次
    // å¯¹è±¡æ± ï¼Œå‡å°‘对象创建
    private final Object packetPoolLock = new Object();
    /**
     * å¯åŠ¨è§£æžå™¨
     */
    public void start() {
        if (isRunning) {
            //System.out.println("串口协议解析器已经在运行中");
            return;
        }
        
@@ -58,8 +70,6 @@
        processorThread = new Thread(this::processPackets, "Serial-Protocol-Parser");
        processorThread.setDaemon(true);
        processorThread.start();
        //System.out.println("串口协议解析器已启动");
    }
    
    /**
@@ -94,11 +104,16 @@
        }
        
        // æ¸…空队列和缓冲区
        clearQueues();
        bufferPosition = 0;
    }
    /**
     * æ¸…空所有队列,释放内存
     */
    private void clearQueues() {
        dataQueue.clear();
        batchQueue.clear();
        bufferPosition = 0;
        //System.out.println("串口协议解析器已停止");
    }
    
    /**
@@ -113,17 +128,39 @@
     */
    public void receiveData(byte[] rawData) {        
        if (!isRunning) {
            //System.out.println("警告: ä¸²å£åè®®è§£æžå™¨æœªå¯åŠ¨ï¼Œå¿½ç•¥æŽ¥æ”¶çš„æ•°æ®");
            return;
            // å¦‚果解析器未运行,自动启动
            start();
        }
        
        if (rawData == null || rawData.length == 0) {
            return;
        }
        
        // æ£€æŸ¥é˜Ÿåˆ—状态,避免内存无限增长
        if (batchQueue.size() > batchQueue.remainingCapacity() * 0.8) {
            // é˜Ÿåˆ—接近满时,丢弃最老的数据
            if (!batchQueue.isEmpty()) {
                batchQueue.poll(); // ç§»é™¤ä¸€ä¸ªæ—§æ•°æ®
            }
        }
        // å°†æ•°æ®æ·»åŠ åˆ°æ‰¹é‡é˜Ÿåˆ—
        if (!batchQueue.offer(rawData)) {
            System.err.println("批量队列已满,丢弃数据");
            // é˜Ÿåˆ—已满时的处理
            handleQueueFull();
        }
    }
    /**
     * å¤„理队列满的情况
     */
    private void handleQueueFull() {
        // æ¸…空队列重新开始,避免内存泄漏
        clearQueues();
        bufferPosition = 0; // é‡ç½®ç¼“冲区
        if (lunxun.DEBUG_ENABLED) {
            SystemDebugDialog.appendAsciiData("警告:数据处理队列已满,已清空队列重新开始");
        }
    }
    
@@ -135,39 +172,105 @@
            return;
        }
        
        // æ‰¹é‡å¤„理数据
        java.util.List<byte[]> batch = new java.util.ArrayList<>(100);
        batchQueue.drainTo(batch, 100);
        for (byte[] rawData : batch) {
            // å°†æ•°æ®æ·»åŠ åˆ°ç¼“å†²åŒº
            if (bufferPosition + rawData.length > dataBuffer.length) {
                // ç¼“冲区不足时,清理并重新开始
                System.arraycopy(dataBuffer, bufferPosition - rawData.length, dataBuffer, 0, rawData.length);
                bufferPosition = rawData.length;
            } else {
        try {
            // æ‰¹é‡å¤„理数据,限制每次处理的最大数量
            int maxBatchSize = 50;
            java.util.List<byte[]> batch = new java.util.ArrayList<>(maxBatchSize);
            batchQueue.drainTo(batch, maxBatchSize);
            for (byte[] rawData : batch) {
                // æ£€æŸ¥ç¼“冲区容量,避免溢出
                if (bufferPosition + rawData.length > dataBuffer.length) {
                    // ç¼“冲区不足时的处理
                    if (rawData.length > dataBuffer.length) {
                        // å•条数据超过缓冲区大小,直接处理或丢弃
                        handleOversizedPacket(rawData);
                        continue;
                    } else {
                        // ç§»åŠ¨æœ‰æ•ˆæ•°æ®åˆ°ç¼“å†²åŒºå¼€å¤´
                        compactBuffer(0);
                    }
                }
                // å°†æ•°æ®æ·»åŠ åˆ°ç¼“å†²åŒº
                System.arraycopy(rawData, 0, dataBuffer, bufferPosition, rawData.length);
                bufferPosition += rawData.length;
                // å¤„理缓冲区中的数据
                processBuffer();
            }
            
            // å¤„理缓冲区中的数据
            processBuffer();
            // å®šæœŸæ£€æŸ¥å†…å­˜
            checkMemory();
        } catch (Exception e) {
            System.err.println("批量处理数据时发生异常: " + e.getMessage());
            // å‘生异常时重置状态
            resetParserState();
        }
        // å®šæœŸæ£€æŸ¥å†…å­˜
        checkMemory();
    }
    /**
     * å¤„理过大的数据包
     */
    private void handleOversizedPacket(byte[] oversizedData) {
        // å¯¹äºŽè¿‡å¤§çš„æ•°æ®åŒ…,直接尝试查找起始标记
        int startIndex = findStartMarkerInArray(oversizedData, 0);
        if (startIndex != -1) {
            // æ‰¾åˆ°èµ·å§‹æ ‡è®°ï¼Œå°†å‰©ä½™æ•°æ®æ”¾å…¥ç¼“冲区
            int remainingLength = oversizedData.length - startIndex;
            if (remainingLength <= dataBuffer.length) {
                System.arraycopy(oversizedData, startIndex, dataBuffer, 0, remainingLength);
                bufferPosition = remainingLength;
                processBuffer();
            }
        }
        // å¦åˆ™ä¸¢å¼ƒè¯¥æ•°æ®åŒ…
    }
    /**
     * åœ¨æŒ‡å®šæ•°ç»„中查找起始标记
     */
    private int findStartMarkerInArray(byte[] data, int startPos) {
        for (int i = startPos; i <= data.length - START_MARKER.length; i++) {
            if (data[i] == START_MARKER[0] && data[i + 1] == START_MARKER[1]) {
                return i;
            }
        }
        return -1;
    }
    /**
     * é‡ç½®è§£æžå™¨çŠ¶æ€
     */
    private void resetParserState() {
        bufferPosition = 0;
        clearQueues();
    }
    
    /**
     * å†…存监控
     */
    private void checkMemory() {
        long currentTime = System.currentTimeMillis();
        if (currentTime - lastMemoryCheckTime < MEMORY_CHECK_INTERVAL) {
            return;
        }
        lastMemoryCheckTime = currentTime;
        Runtime runtime = Runtime.getRuntime();
        long usedMem = runtime.totalMemory() - runtime.freeMemory();
        long maxMem = runtime.maxMemory();
        
        if (usedMem > maxMem * 0.8) {
            //System.out.println("内存使用率超过80%,当前使用: " + (usedMem / 1024 / 1024) + "MB");
        if (usedMem > maxMem * 0.75) {
            // å†…存使用率超过75%时进行垃圾回收
            System.gc();
            if (lunxun.DEBUG_ENABLED) {
                SystemDebugDialog.appendAsciiData("内存使用率超过75%,已触发垃圾回收。使用内存: " +
                    (usedMem / 1024 / 1024) + "MB");
            }
        }
    }
    
@@ -175,7 +278,10 @@
     * å¤„理缓冲区中的数据,解析完整数据包
     */
    private void processBuffer() {
        while (bufferPosition >= MIN_PACKET_LENGTH) {
        int processedCount = 0;
        final int MAX_PACKETS_PER_BATCH = 20; // é™åˆ¶æ¯æ¬¡å¤„理的最大包数量
        while (bufferPosition >= MIN_PACKET_LENGTH && processedCount < MAX_PACKETS_PER_BATCH) {
            // æŸ¥æ‰¾èµ·å§‹æ ‡è®°
            int startIndex = findStartMarker();
            if (startIndex == -1) {
@@ -195,6 +301,13 @@
            int dataLength = ((dataBuffer[startIndex + 2] & 0xFF) << 8) | (dataBuffer[startIndex + 3] & 0xFF);
            int totalPacketLength = 2 + 2 + dataLength + 2; // èµ·å§‹æ ‡è®°2 + æ•°æ®é•¿åº¦2 + æ•°æ®å†…容 + CRC2
            
            // æ£€æŸ¥æ•°æ®é•¿åº¦æ˜¯å¦åˆç†
            if (dataLength < 0 || totalPacketLength > dataBuffer.length) {
                // æ•°æ®é•¿åº¦å¼‚常,跳过这个包
                compactBuffer(startIndex + 1);
                continue;
            }
            // æ£€æŸ¥æ˜¯å¦æ”¶åˆ°å®Œæ•´æ•°æ®åŒ…
            if (startIndex + totalPacketLength > bufferPosition) {
                // æ•°æ®åŒ…不完整,等待更多数据
@@ -203,16 +316,15 @@
            }
            
            // æå–完整数据包
            byte[] packet = new byte[totalPacketLength];
            System.arraycopy(dataBuffer, startIndex, packet, 0, totalPacketLength);
            // å°†æ•°æ®åŒ…放入队列供解析
            try {
            byte[] packet = extractPacket(startIndex, totalPacketLength);
            if (packet != null) {
                // å°†æ•°æ®åŒ…放入队列供解析
                if (!dataQueue.offer(packet)) {
                    System.err.println("数据队列已满,丢弃数据包");
                    // é˜Ÿåˆ—已满,释放packet引用
                    packet = null;
                    handleDataQueueFull();
                    return;
                }
            } catch (Exception e) {
                System.err.println("放入数据队列时发生异常: " + e.getMessage());
            }
            
            // ç§»åŠ¨ç¼“å†²åŒºä½ç½®
@@ -221,6 +333,29 @@
                System.arraycopy(dataBuffer, startIndex + totalPacketLength, dataBuffer, 0, remaining);
            }
            bufferPosition = remaining;
            processedCount++;
        }
    }
    /**
     * æå–数据包,重用byte数组减少对象创建
     */
    private byte[] extractPacket(int startIndex, int totalPacketLength) {
        byte[] packet = new byte[totalPacketLength];
        System.arraycopy(dataBuffer, startIndex, packet, 0, totalPacketLength);
        return packet;
    }
    /**
     * å¤„理数据队列满的情况
     */
    private void handleDataQueueFull() {
        // ä¸¢å¼ƒé˜Ÿåˆ—中最老的数据
        dataQueue.poll();
        if (lunxun.DEBUG_ENABLED) {
            SystemDebugDialog.appendAsciiData("数据解析队列已满,丢弃最老数据包");
        }
    }
    
@@ -240,7 +375,7 @@
     * åŽ‹ç¼©ç¼“å†²åŒºï¼Œå°†æœ‰æ•ˆæ•°æ®ç§»åˆ°å¼€å¤´
     */
    private void compactBuffer(int startIndex) {
        if (startIndex > 0) {
        if (startIndex > 0 && bufferPosition > startIndex) {
            System.arraycopy(dataBuffer, startIndex, dataBuffer, 0, bufferPosition - startIndex);
            bufferPosition -= startIndex;
        }
@@ -250,43 +385,46 @@
     * å¤„理数据包的主方法
     */
    private void processPackets() {
        //System.out.println("串口数据包处理线程开始运行");
        System.out.println("串口数据包处理线程开始运行");
        
        while (isRunning && !Thread.currentThread().isInterrupted()) {
            try {
                byte[] packet = dataQueue.take(); // é˜»å¡žç›´åˆ°æœ‰æ•°æ®
                parsePacket(packet);
            } catch (InterruptedException e) {
                //System.out.println("串口数据包处理线程被中断");
                System.out.println("串口数据包处理线程被中断");
                Thread.currentThread().interrupt();
                break;
            } catch (Exception e) {
                System.err.println("处理数据包时发生异常: " + e.getMessage());
                e.printStackTrace();
                // ç»§ç»­è¿è¡Œï¼Œä¸é€€å‡ºçº¿ç¨‹
            }
        }
        
        //System.out.println("串口数据包处理线程结束运行");
        System.out.println("串口数据包处理线程结束运行");
    }
    
    /**
     * è§£æžæ•°æ®åŒ…并根据功能码调用相应方法
     */
    private void parsePacket(byte[] packet) {
        if (packet == null || packet.length < MIN_PACKET_LENGTH) {
            return;
        }
        try {
            SerialPortService.getReceivedDataCount();
            SerialPortService.getReceivedDataCount();
            // è§£æžåŸºæœ¬å­—段
            byte hostAddress = packet[4];        // ä¸»æœºåœ°å€
            byte slotAddress = packet[5];        // å¡æ§½åœ°å€
            byte functionCode = packet[6];       // åŠŸèƒ½ç 
            byte functionCode = packet[6];       // åŠŸèƒ½ç 
            // æ•°æ®é•¿åº¦ (从协议中读取)
            int dataLength = ((packet[2] & 0xFF) << 8) | (packet[3] & 0xFF);
            
            // è¿”回值数据
            int returnValueLength = dataLength - 3; // N-3 (减去主机地址、卡槽地址、功能码)
            byte[] returnValue = null;
            if (returnValueLength > 0) {
            if (returnValueLength > 0 && returnValueLength <= packet.length - 7) {
                returnValue = new byte[returnValueLength];
                System.arraycopy(packet, 7, returnValue, 0, returnValueLength);
            }
@@ -297,43 +435,51 @@
                    if (returnValue != null) {
                        // ä½¿ç”¨ä¼˜åŒ–的字节数组解析方法,避免字符串转换
                        ParseResult rst = ProtocolParser01.parseDDCC01Data(packet);
                        rst.fuzhi();
//                        rst.toString();
                        if (rst != null) {
                            rst.fuzhi();
                            if (lunxun.DEBUG_ENABLED) {
                                SystemDebugDialog.appendAsciiData(rst.toString());
                            }
                        }
                    }
                    break;
                case FUNCTION_51:
                    // è°ƒç”¨ ProtocolParser51 å¤„理数据
                    String hexPacket = bytesToHex(packet);
                    int result = ProtocolParser51.parse(hexPacket);
                    int slot = slotAddress;
                    if (result == 1) {
                        //System.out.println("功能码 0x51 - å¼€é—¨æŽ§åˆ¶æˆåŠŸ");
                        SlotManager.changgehaska(slot, result);
                    } else {
                        //System.out.println("功能码 0x51 - å¼€é—¨æŽ§åˆ¶å¤±è´¥æˆ–报文不合法");
                        String message = slot + "号卡槽取卡失败";
                        Charulog.logOperation(message);
                    }
                    break;
                case FUNCTION_52:
                    //System.out.println("功能码 0x52 - LED亮度控制");
                    // LED亮度控制 - æ— æ“ä½œ
                    break;
                case FUNCTION_80:
                    //System.out.println("功能码 0x80 - å·¥å¡å‡çº§ä½¿èƒ½");
                    // å·¥å¡å‡çº§ä½¿èƒ½ - æ— æ“ä½œ
                    break;
                case FUNCTION_81:
                    //System.out.println("功能码 0x81 - å·¥ä½œå¡å‡çº§æ•°æ®åŒ…");
                    // å·¥ä½œå¡å‡çº§æ•°æ®åŒ… - æ— æ“ä½œ
                    break;
                case FUNCTION_82:
                    //System.out.println("功能码 0x82 - å•板升级使能");
                    // å•板升级使能 - æ— æ“ä½œ
                    break;
                case FUNCTION_83:
                    //System.out.println("功能码 0x83 - å•板升级数据包");
                    // å•板升级数据包 - æ— æ“ä½œ
                    break;
                default:
                    System.err.println("未知功能码: 0x" + Integer.toHexString(functionCode & 0xFF));
                    if (lunxun.DEBUG_ENABLED) {
                        System.err.println("未知功能码: 0x" + Integer.toHexString(functionCode & 0xFF));
                    }
                    break;
            }
            
        } catch (Exception e) {
            System.err.println("解析数据包时发生错误: " + e.getMessage());
            e.printStackTrace();
            // ä¸æ‰“印堆栈跟踪,避免产生大量日志对象
        }
    }
    
@@ -341,6 +487,10 @@
     * ä¼˜åŒ–的字节数组转十六进制字符串方法
     */
    private String bytesToHex(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return "";
        }
        hexBuilder.setLength(0); // æ¸…空重用
        for (byte b : bytes) {
            hexBuilder.append(String.format("%02X", b));
@@ -379,12 +529,14 @@
     * èŽ·å–è§£æžå™¨çŠ¶æ€ä¿¡æ¯
     */
    public String getStatusInfo() {
        return String.format("串口解析器状态: %s, é˜Ÿåˆ—大小: %d/%d, æ‰¹é‡é˜Ÿåˆ—: %d/%d",
        return String.format("串口解析器状态: %s, é˜Ÿåˆ—大小: %d/%d, æ‰¹é‡é˜Ÿåˆ—: %d/%d, ç¼“冲区: %d/%d",
                           isRunning ? "运行中" : "已停止", 
                           dataQueue.size(), 
                           dataQueue.remainingCapacity() + dataQueue.size(),
                           batchQueue.size(),
                           batchQueue.remainingCapacity() + batchQueue.size());
                           batchQueue.remainingCapacity() + batchQueue.size(),
                           bufferPosition,
                           dataBuffer.length);
    }
    
    /**
@@ -393,4 +545,14 @@
    public void setMaxRawDataPrintLength(int length) {
        // å®žçŽ°æ ¹æ®éœ€è¦è°ƒæ•´
    }
    /**
     * ä¸»åŠ¨æ¸…ç†èµ„æº
     */
    public void cleanup() {
        stop();
        clearQueues();
        bufferPosition = 0;
        hexBuilder.setLength(0);
    }
}
src/xitongshezhi/ConfigSet.java
@@ -54,13 +54,13 @@
    private final MenuItemListener menuItemListener;
    
    public ConfigSet(JFrame parent) {
        super(parent, "设置", true);
        super(parent, "", true);
        configManager = Chushihua.getInstance();
        menuItemListener = new MenuItemListener();
     // è®°å½•进入设置页面前的轮询状态
        // è®°å½•进入设置页面前的轮询状态
        recordPollingStateBeforeEntering();
        initializeUI();
     // è¿›å…¥è®¾ç½®é¡µé¢æ—¶æš‚停轮询
        // è¿›å…¥è®¾ç½®é¡µé¢æ—¶æš‚停轮询
        pausePollingWhenEntering();
    }
    
@@ -135,7 +135,26 @@
        mainPanel.add(createHeaderPanel(), BorderLayout.NORTH);
        mainPanel.add(createMenuGridPanel(), BorderLayout.CENTER);
        
        // æ·»åŠ ç‰ˆæƒä¿¡æ¯åœ¨åº•éƒ¨
        mainPanel.add(createCopyrightPanel(), BorderLayout.SOUTH);
        getContentPane().add(mainPanel);
    }
 // åˆ›å»ºç‰ˆæƒä¿¡æ¯é¢æ¿
    private JPanel createCopyrightPanel() {
        JPanel copyrightPanel = new JPanel();
        copyrightPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
        copyrightPanel.setOpaque(true);
        copyrightPanel.setBackground(DARK_COLOR);
        copyrightPanel.setBorder(new EmptyBorder(10, 0, 5, 0));
        JLabel copyrightLabel = new JLabel("北京华星北斗智控技术有限公司 ç‰ˆæƒæ‰€æœ‰ 2025");
        copyrightLabel.setFont(new Font("Microsoft YaHei", Font.PLAIN, 12));
        copyrightLabel.setForeground(new Color(160, 160, 160));
        copyrightPanel.add(copyrightLabel);
        return copyrightPanel;
    }
    
    private JPanel createHeaderPanel() {
@@ -158,8 +177,11 @@
        closeButton.setOpaque(true); // ç¡®ä¿ä¸é€æ˜Ž
        closeButton.setFocusPainted(false);
        closeButton.setBorder(BorderFactory.createEmptyBorder(10, 18, 10, 18));
        closeButton.setIcon(getCachedIcon("✕", 16));
        closeButton.addActionListener(e -> dispose());
        closeButton.addActionListener(e -> {
            // å¼ºåˆ¶å¼€å¯è½®è¯¢æŸ¥è¯¢åŠŸèƒ½
            forceStartPolling();
            dispose();
        });
        
        // æ·»åŠ æ‚¬åœæ•ˆæžœ
        closeButton.addMouseListener(new java.awt.event.MouseAdapter() {
@@ -588,12 +610,13 @@
        return icon;
    }
    
    private void showSystemDebugDialog() {
        SystemDebugDialog.showSystemDebugDialog((JFrame) getParent());
    }
    
    @Override
    public void dispose() {
        // é€€å‡ºè®¾ç½®é¡µé¢æ—¶æ¢å¤è½®è¯¢æŽ§åˆ¶
        SystemDebugDialog.setPollingControlEnabled(true);
        // é€€å‡ºè®¾ç½®é¡µé¢æ—¶æ¢å¤è½®è¯¢
        resumePollingWhenExiting();
        
@@ -673,5 +696,43 @@
            configDialog.setVisible(true);
        });
    }    
    /**
     * å¼ºåˆ¶å¼€å¯è½®è¯¢æŸ¥è¯¢åŠŸèƒ½ï¼ˆæ— è®ºå½“å‰çŠ¶æ€å¦‚ä½•ï¼‰
     */
    private void forceStartPolling() {
        try {
            // å¦‚果轮询未运行,则启动轮询
            if (!lunxun.isPolling()) {
                boolean started = lunxun.startPolling();
                if (started) {
                    //System.out.println("强制开启:轮询查询已启动");
                } else {
                    System.err.println("强制开启:启动轮询查询失败");
                }
            }
            // å¦‚果轮询已运行但处于暂停状态,则恢复轮询
            else if (lunxun.isPaused()) {
                boolean resumed = lunxun.resumePolling();
                if (resumed) {
                    //System.out.println("强制开启:轮询查询已恢复");
                } else {
                    System.err.println("强制开启:恢复轮询查询失败");
                }
            }
            // å¦‚果轮询已运行且未暂停,则无需操作
            else {
                //System.out.println("强制开启:轮询查询已在运行中");
            }
        } catch (Exception e) {
            System.err.println("强制开启轮询查询时发生异常: " + e.getMessage());
        }
    }
    
 // åœ¨ showSystemDebugDialog æ–¹æ³•中添加
    private void showSystemDebugDialog() {
        // åœ¨æ‰“开调试对话框时禁用轮询控制
        SystemDebugDialog.setPollingControlEnabled(false);
        SystemDebugDialog.showSystemDebugDialog((JFrame) getParent());
    }
}
src/xitongshezhi/SystemDebugDialog.java
@@ -10,6 +10,7 @@
import java.text.SimpleDateFormat;
import java.util.Date;
import chushihua.lunxun;
import dialog.Errlog;
public class SystemDebugDialog extends JDialog {
    private static final long serialVersionUID = -9131186661220052051L;
@@ -25,11 +26,9 @@
    private static final Color WARNING_COLOR = new Color(243, 156, 18);
    private static final Color DARK_COLOR = new Color(15, 28, 48);
    private static final Color TEXT_COLOR = new Color(224, 224, 224);
    // UI组件
 // UI组件
    private JTextArea dataTextArea;
    private JButton clearButton;
    private JButton pollingButton;
    private JButton scrollButton;
    private JTextField sendTextField;
    private JButton sendButton;
@@ -43,7 +42,7 @@
    private SerialPortService serialService;
    
    // æŽ§åˆ¶å˜é‡
    private boolean autoScroll = true; // é»˜è®¤è‡ªåŠ¨æ»šåŠ¨
    private boolean autoScroll =false; // é»˜è®¤è‡ªåŠ¨æ»šåŠ¨
    private static boolean dataUpdateEnabled = true; // æŽ§åˆ¶æ•°æ®æ›´æ–°
    
    // æ—¥æœŸæ ¼å¼åŒ–
@@ -214,14 +213,6 @@
        scrollButton.setBorder(BorderFactory.createEmptyBorder(8, 20, 8, 20));
        scrollButton.addActionListener(e -> toggleAutoScroll());
        
        // å¯åЍ/关闭查询按钮
        pollingButton = new JButton("启动查询");
        pollingButton.setFont(new Font("Microsoft YaHei", Font.BOLD, 14));
        pollingButton.setBackground(SECONDARY_COLOR);
        pollingButton.setForeground(Color.WHITE);
        pollingButton.setFocusPainted(false);
        pollingButton.setBorder(BorderFactory.createEmptyBorder(8, 20, 8, 20));
        pollingButton.addActionListener(e -> togglePolling());
        
        // æ¸…空按钮
        clearButton = new JButton("清空数据");
@@ -233,7 +224,6 @@
        clearButton.addActionListener(e -> clearData());
        
        buttonPanel.add(scrollButton);
        buttonPanel.add(pollingButton);
        buttonPanel.add(clearButton);
        
        return buttonPanel;
@@ -331,40 +321,23 @@
        staticDataUpdateEnabled = dataUpdateEnabled; // åŒæ­¥é™æ€å˜é‡
        if (autoScroll) {
            scrollButton.setText("暂停");
            scrollButton.setText("开始");
            scrollButton.setBackground(PRIMARY_COLOR);
            if (staticDataTextArea != null) {
                staticDataTextArea.setCaretPosition(staticDataTextArea.getDocument().getLength());
            }
            appendAsciiData("[" + getCurrentTime() + "] å¼€å§‹æ˜¾ç¤ºæ•°æ®\n");
            lunxun.resumePolling();
            lunxun.DEBUG_ENABLED=true;
        } else {
            scrollButton.setText("开始");
            scrollButton.setText("暂停");
            scrollButton.setBackground(WARNING_COLOR);
            appendAsciiData("[" + getCurrentTime() + "] æš‚停显示数据\n");
        }
    }
    
    private void togglePolling() {
        if (lunxun.isPolling()) {
            // å¦‚果正在轮询,则停止
            if (lunxun.stopPolling()) {
                pollingButton.setText("启动查询");
                pollingButton.setBackground(SECONDARY_COLOR);
                onDataReceivedascii("[" + getCurrentTime() + "] stop\n");
                lunxun.setDEBUG_ENABLED(false);
            }
        } else {
            // å¦‚果未轮询,则启动
            if (lunxun.startPolling()) {
                lunxun.setDEBUG_ENABLED(true);
                pollingButton.setText("停止查询");
                pollingButton.setBackground(DANGER_COLOR);
                onDataReceivedascii("[" + getCurrentTime() + "] strat\n");
            } else {
                showMessage("错误", "无法启动轮询查询,请检查串口连接", "error");
            }
        }
    }
    
    private void sendData() {
        String text = sendTextField.getText().trim();
@@ -418,7 +391,7 @@
                dataTextArea.setText(sb.toString());
            }
        } catch (Exception e) {
            System.err.println("修剪行数时发生错误: " + e.getMessage());
            Errlog.logOperation("修剪行数时发生错误: " + e.getMessage());
        }
    }
    
@@ -479,7 +452,7 @@
                staticDataTextArea.setText(sb.toString());
            }
        } catch (Exception e) {
            System.err.println("修剪行数时发生错误: " + e.getMessage());
            Errlog.logOperation("修剪行数时发生错误: " + e.getMessage());
        }
    }
@@ -511,7 +484,7 @@
                }
            } catch (Exception e) {
                System.err.println("显示数据时发生错误: " + e.getMessage());
                Errlog.logOperation("显示数据时发生错误: " + e.getMessage());
            }
        });
    }
@@ -531,14 +504,14 @@
                    displayText = displayText.substring(0, MAX_LINE_LENGTH) + "...";
                }
                staticDataTextArea.append(displayText);
                staticDataTextArea.append(displayText+"\n");
                if (staticDataUpdateEnabled) {
                    staticDataTextArea.setCaretPosition(staticDataTextArea.getDocument().getLength());
                }
            } catch (Exception e) {
                System.err.println("显示ASCII数据时发生错误: " + e.getMessage());
                Errlog.logOperation("显示ASCII数据时发生错误: " + e.getMessage());
            }
        });
    }
@@ -616,14 +589,13 @@
            memoryMonitorTimer.stop();
        }
        
        // å…³é—­æ—¶åœæ­¢è½®è¯¢
        if (lunxun.isPolling()) {
            lunxun.stopPolling();
        }
        // é‡è¦ä¿®æ”¹ï¼šä¸è¦åœæ­¢è½®è¯¢ï¼Œåªæš‚停调试输出
        lunxun.setDEBUG_ENABLED(false);
        
        // åœæ­¢ä¸²å£æ•°æ®æ•获
        // é‡è¦ä¿®æ”¹ï¼šä¸è¦åœæ­¢ä¸²å£æ•°æ®æ•获,只移除自己的回调
        if (serialService != null) {
            serialService.stopCapture();
            // ä½¿ç”¨æ–°çš„æ–¹æ³•移除回调而不停止整个捕获
            removeDataReceivedCallback();
        }
        
        // æ¸…理静态引用,防止内存泄漏
@@ -634,4 +606,21 @@
        
        super.dispose();
    }
    // æ–°å¢žï¼šç§»é™¤æ•°æ®æŽ¥æ”¶å›žè°ƒçš„æ–¹æ³•
    private void removeDataReceivedCallback() {
        if (serialService != null) {
            try {
                // é€šè¿‡åå°„或其他方式移除回调,或者简单地设置为null
                // è¿™é‡Œå‡è®¾SerialPortService有移除回调的方法
                serialService.setResponseConsumer(null);
            } catch (Exception e) {
                Errlog.logOperation("移除数据接收回调时发生错误: " + e.getMessage());
            }
        }
    }
    // æ–°å¢žï¼šå¯ç”¨/禁用轮询控制的方法
    public static void setPollingControlEnabled(boolean enabled) {
    }
}
src/xitongshezhi/kacaoguanli.java
@@ -12,6 +12,7 @@
import java.util.Random;
import chushihua.SlotManager;
import home.Fkj;
public class kacaoguanli extends JDialog {
    // å±å¹•尺寸常量 - é€‚配7寸竖屏
src/xitongshezhi/kuaisuquka.java
@@ -1,5 +1,4 @@
package xitongshezhi;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
@@ -10,30 +9,23 @@
import java.util.List;
import java.util.Map;
// æ–°å¢žå¯¼å…¥
import publicway.OpenDoor;
import chuankou.Sendmsg;
import chushihua.SlotManager;
import home.Fkj;
@SuppressWarnings("serial")
public class kuaisuquka extends JDialog {
    // å±å¹•尺寸常量 - é€‚配7寸竖屏
    private static final int SCREEN_WIDTH = 600;
    private static final int SCREEN_HEIGHT = 1024;
    // æ–°å¢žï¼šè®°å½•进入快速取卡页面前的轮询状态
    private boolean wasPollingRunning = false;
    private boolean wasPollingPaused = false;
    // é¢œè‰²å¸¸é‡
    private static final Color PRIMARY_COLOR = new Color(52, 152, 219);
    private static final Color PRIMARY_DARK_COLOR = new Color(41, 128, 185);
    private static final Color SECONDARY_COLOR = new Color(46, 204, 113);
    private static final Color DANGER_COLOR = new Color(231, 76, 60);
    private static final Color WARNING_COLOR = new Color(243, 156, 18);
    private static final Color DARK_COLOR = new Color(15, 28, 48);
    private static final Color DARK_LIGHT_COLOR = new Color(26, 43, 68);
    private static final Color TEXT_COLOR = new Color(224, 224, 224);
    private static final Color TEXT_LIGHT_COLOR = new Color(160, 200, 255);
    private static final Color CARD_BG_COLOR = new Color(30, 60, 114, 178);
    // ä¼˜åŒ–的颜色常量
    private static final Color BRIGHT_GREEN = new Color(46, 204, 113);  // æœ‰å¡ - ç»¿è‰²
    private static final Color DARK_GRAY = new Color(93, 109, 126);     // æ— å¡ - æ·±ç°è‰²
@@ -46,16 +38,10 @@
        HAS_CARD("{卡号}", BRIGHT_GREEN),  // å ä½ç¬¦ï¼Œå®žé™…显示时会替换为具体卡号
        NO_CARD("无卡", DARK_GRAY);
        
        private final String displayName;
        private final Color color;
        
        SlotStatus(String displayName, Color color) {
            this.displayName = displayName;
            this.color = color;
        }
        public String getDisplayName() {
            return displayName;
        }
        
        public Color getColor() {
@@ -72,34 +58,47 @@
    // ç»Ÿè®¡ä¿¡æ¯
    private JLabel cardsCountLabel;
    
    // ä¼˜åŒ–的对话框管理
    private JDialog progressDialog;
    private JProgressBar progressBar;
    private JLabel progressLabel;
    private JDialog resultDialog;
    // å…±äº«çš„事件监听器
    private final SlotButtonListener slotButtonListener;
    
    private SlotManager slotManager;
    private Timer refreshTimer; // æ–°å¢žï¼šåˆ·æ–°å®šæ—¶å™¨
    public kuaisuquka(JFrame parent) {
        super(parent, "快速取卡", true);
        slotManager = new SlotManager();
        slotButtons = new ArrayList<>(60);
        slotStatuses = new ArrayList<>(60);
        slotButtonListener = new SlotButtonListener();
        
        // è®°å½•进入快速取卡页面前的轮询状态
        recordPollingStateBeforeEntering();
        initializeUI();
        initializeSlots();
        startAutoRefresh(); // æ–°å¢žï¼šå¯åŠ¨è‡ªåŠ¨åˆ·æ–°
        startAutoRefresh();
        
        // è¿›å…¥å¿«é€Ÿå–卡页面时暂停轮询
        pausePollingWhenEntering();
        // è°ƒè¯•信息
        //System.out.println("快速取卡页面初始化完成,开始显示卡槽状态");
        debugSlotStatus();
    }
    /**
     * è°ƒè¯•方法:打印卡槽状态信息
     */
    private void debugSlotStatus() {
        Fkj[] slotArray = SlotManager.getSlotArray();
        if (slotArray == null) {
            //System.out.println("SlotManager.getSlotArray() è¿”回 null");
            return;
        }
        //System.out.println("=== å¡æ§½çŠ¶æ€è°ƒè¯•ä¿¡æ¯ ===");
        for (int i = 0; i < Math.min(slotArray.length, 60); i++) {
            Fkj slot = slotArray[i];
            if (slot != null) {
                System.out.printf("卡槽 %d: hasCard=%s, cardNumber=%s%n",
                    i + 1, slot.getHasCard(), slot.getCardNumber());
            } else {
                System.out.printf("卡槽 %d: null%n", i + 1);
            }
        }
        //System.out.println("======================");
    }
    
    /**
@@ -116,74 +115,108 @@
    }
    
    /**
     * ä»ŽSlotManager刷新卡槽状态
     * ä»ŽSlotManager刷新卡槽状态 - å½»åº•重写版本
     */
    private void refreshSlotStatusFromManager() {
        boolean statusChanged = false;
        Fkj[] slotArray = SlotManager.getSlotArray();
        
        for (int i = 0; i < 60; i++) {
            int slotId = i + 1;
            String hasCardStatus = SlotManager.getSlotHasCardStatus(slotId);
        if (slotArray == null) {
            //System.out.println("刷新卡槽状态: slotArray ä¸º null");
            return;
        }
        for (int i = 0; i < 60 && i < slotArray.length; i++) {
            Fkj slotInfo = slotArray[i];
            if (slotInfo == null) {
                // å¦‚果卡槽信息为null,设置为无卡状态
                if (i < slotStatuses.size() && slotStatuses.get(i) != SlotStatus.NO_CARD) {
                    slotStatuses.set(i, SlotStatus.NO_CARD);
                    statusChanged = true;
                }
                continue;
            }
            
            // æ£€æŸ¥æ˜¯å¦æœ‰å¡å¹¶ä¸”有有效的卡号
            String hasCardStatus = slotInfo.getHasCard();
            String cardNumber = slotInfo.getCardNumber();
            // ç®€åŒ–的判断逻辑 - é‡ç‚¹è°ƒè¯•
            boolean reallyHasCard = false;
            if ("1".equals(hasCardStatus)) {
                Fkj slotInfo = slotManager.getSlotInfo(slotId);
                String cardNumber = slotInfo != null ? slotInfo.getCardNumber() : "-1";
                // åªæœ‰å½“卡号不是-1、null且不为空时,才认为真正有卡
                reallyHasCard = !("-1".equals(cardNumber) && cardNumber != null && !cardNumber.trim().isEmpty());
                // å¦‚æžœhasCard为"1",进一步检查卡号
                if (cardNumber != null && !cardNumber.trim().isEmpty()) {
                    // æ”¾å®½æ¡ä»¶ï¼šåªè¦å¡å·ä¸ä¸ºç©ºä¸”不是"0000"和"-1",就认为有卡
                    reallyHasCard = !"0000".equals(cardNumber) && !"-1".equals(cardNumber);
                } else {
                    // å¡å·ä¸ºnull或空,但有卡状态为1,显示为有卡但卡号未知
                    reallyHasCard = true;
                }
            }
            
            SlotStatus newStatus = reallyHasCard ? SlotStatus.HAS_CARD : SlotStatus.NO_CARD;
            SlotStatus currentStatus = slotStatuses.get(i);
            
            // æ£€æŸ¥çŠ¶æ€æ˜¯å¦å‘ç”Ÿå˜åŒ–
            if (newStatus != currentStatus) {
                slotStatuses.set(i, newStatus);
            // ç¡®ä¿slotStatuses有足够的元素
            if (i >= slotStatuses.size()) {
                slotStatuses.add(newStatus);
                statusChanged = true;
            } else {
                SlotStatus currentStatus = slotStatuses.get(i);
                if (newStatus != currentStatus) {
                    slotStatuses.set(i, newStatus);
                    statusChanged = true;
                    // è°ƒè¯•状态变化
                    System.out.printf("卡槽 %d çŠ¶æ€å˜åŒ–: %s -> %s (hasCard=%s, cardNumber=%s)%n",
                        i + 1, currentStatus, newStatus, hasCardStatus, cardNumber);
                }
            }
            // æ›´æ–°æŒ‰é’®æ˜¾ç¤º
            updateSlotButtonDisplay(i, slotInfo, newStatus);
        }
        
        // å¦‚果状态发生变化,更新显示
        // å¦‚果状态发生变化,更新统计信息
        if (statusChanged) {
            updateCardSlotsDisplay();
            updateStatistics();
        } else {
            // å³ä½¿çŠ¶æ€æ²¡å˜ï¼Œä¹Ÿè¦æ£€æŸ¥å¡å·æ˜¯å¦æ›´æ–°
            updateCardSlotsDisplay();
        }
    }
    /**
     * è®°å½•进入快速取卡页面前的轮询状态
     */
    private void recordPollingStateBeforeEntering() {
        wasPollingRunning = chushihua.lunxun.isPolling();
        wasPollingPaused = chushihua.lunxun.isPaused();
        //System.out.println("进入快速取卡页面,记录轮询状态 - è¿è¡Œ: " + wasPollingRunning + ", æš‚停: " + wasPollingPaused);
    }
    /**
     * è¿›å…¥å¿«é€Ÿå–卡页面时暂停轮询
     */
    private void pausePollingWhenEntering() {
        if (chushihua.lunxun.isPolling() && !chushihua.lunxun.isPaused()) {
            chushihua.lunxun.pausePolling();
            //System.out.println("进入快速取卡页面,轮询已暂停");
        }
    }
    
    /**
     * é€€å‡ºå¿«é€Ÿå–卡页面时恢复轮询
     */
    private void resumePollingWhenExiting() {
        // åªæœ‰è¿›å…¥å¿«é€Ÿå–卡页面时轮询是运行状态且未被暂停,才恢复轮询
        if (wasPollingRunning && !wasPollingPaused) {
            if (chushihua.lunxun.isPolling() && chushihua.lunxun.isPaused()) {
                chushihua.lunxun.resumePolling();
                //System.out.println("退出快速取卡页面,轮询已恢复");
    private void updateSlotButtonDisplay(int index, Fkj slotInfo, SlotStatus status) {
        if (index < 0 || index >= slotButtons.size()) {
            return;
        }
        JButton slotButton = slotButtons.get(index);
        // æ›´æ–°æŒ‰é’®èƒŒæ™¯é¢œè‰²
        slotButton.setBackground(status.getColor());
        // æ›´æ–°çŠ¶æ€æ ‡ç­¾
        Component[] components = slotButton.getComponents();
        for (Component comp : components) {
            if (comp instanceof JLabel) {
                JLabel label = (JLabel) comp;
                // æ‰¾åˆ°çŠ¶æ€æ ‡ç­¾ï¼ˆè¾ƒå°çš„å­—ä½“ï¼‰
                if (label.getFont().getSize() == 11) {
                    String displayText;
                    if (status == SlotStatus.HAS_CARD) {
                        // æ˜¾ç¤ºå®žé™…卡号,如果卡号无效则显示"有卡"
                        String cardNumber = slotInfo.getCardNumber();
                        if (cardNumber != null && !cardNumber.trim().isEmpty() &&
                            !"0000".equals(cardNumber) && !"-1".equals(cardNumber)) {
                            displayText = cardNumber;
                        } else {
                            displayText = "有卡";
                        }
                    } else {
                        displayText = "无卡";
                    }
                    label.setText(displayText);
                    break;
                }
            }
        } else {
            //System.out.println("退出快速取卡页面,保持原有轮询状态 - è¿è¡Œ: " + wasPollingRunning + ", æš‚停: " + wasPollingPaused);
        }
    }
    
@@ -285,7 +318,6 @@
        openAllButton.setForeground(Color.WHITE);
        openAllButton.setFocusPainted(false);
        openAllButton.setBorder(BorderFactory.createEmptyBorder(12, 24, 12, 24));
        openAllButton.setIcon(getCachedIcon("🚪", 20));
        openAllButton.addActionListener(e -> openAllSlots());
        
        // æ·»åŠ æ‚¬åœæ•ˆæžœ
@@ -347,53 +379,98 @@
    }
    
    private void initializeSlots() {
        // ä»Ž SlotManager èŽ·å–å¡æ§½çŠ¶æ€
        for (int i = 0; i < 60; i++) {
            int slotId = i + 1;
            String hasCardStatus = SlotManager.getSlotHasCardStatus(slotId);
            // æ£€æŸ¥æ˜¯å¦æœ‰å¡å¹¶ä¸”有有效的卡号
            boolean reallyHasCard = false;
            if ("1".equals(hasCardStatus)) {
                Fkj slotInfo = slotManager.getSlotInfo(slotId);
                String cardNumber = slotInfo != null ? slotInfo.getCardNumber() : "-1";
                // åªæœ‰å½“卡号不是-1、null且不为空时,才认为真正有卡
                reallyHasCard = !("-1".equals(cardNumber) && cardNumber != null && !cardNumber.trim().isEmpty());
        // æ¸…空现有状态
        slotStatuses.clear();
        // ç›´æŽ¥ä»Ž SlotManager èŽ·å–æœ€æ–°æ•°æ®
        Fkj[] slotArray = SlotManager.getSlotArray();
        //System.out.println("初始化卡槽状态,slotArray: " + (slotArray != null ? "非空" : "空"));
        if (slotArray != null) {
            for (int i = 0; i < 60 && i < slotArray.length; i++) {
                Fkj slotInfo = slotArray[i];
                SlotStatus status = SlotStatus.NO_CARD;
                if (slotInfo != null) {
                    String hasCardStatus = slotInfo.getHasCard();
                    String cardNumber = slotInfo.getCardNumber();
                    // ç®€åŒ–的判断逻辑
                    boolean reallyHasCard = false;
                    if ("1".equals(hasCardStatus)) {
                        if (cardNumber != null && !cardNumber.trim().isEmpty()) {
                            reallyHasCard = !"0000".equals(cardNumber) && !"-1".equals(cardNumber);
                        } else {
                            // å¡å·ä¸ºnull或空,但有卡状态为1,显示为有卡
                            reallyHasCard = true;
                        }
                    }
                    status = reallyHasCard ? SlotStatus.HAS_CARD : SlotStatus.NO_CARD;
                    // è°ƒè¯•前几个卡槽
                    if (i < 5) {
                        System.out.printf("初始化卡槽 %d: hasCard=%s, cardNumber=%s, åˆ¤æ–­ä¸º: %s%n",
                            i + 1, hasCardStatus, cardNumber, status);
                    }
                }
                slotStatuses.add(status);
            }
            SlotStatus status = reallyHasCard ? SlotStatus.HAS_CARD : SlotStatus.NO_CARD;
            slotStatuses.add(status);
        } else {
            // å¦‚æžœslotArray为null,全部初始化为无卡
            //System.out.println("slotArray为null,全部初始化为无卡");
            for (int i = 0; i < 60; i++) {
                slotStatuses.add(SlotStatus.NO_CARD);
            }
        }
        
        createCardSlots();
        updateStatistics();
        // ç«‹å³åˆ·æ–°ä¸€æ¬¡çŠ¶æ€
        refreshSlotStatusFromManager();
    }
    
    private void createCardSlots() {
        cardSlotsPanel.removeAll();
        slotButtons.clear();
        
        Fkj[] slotArray = SlotManager.getSlotArray();
        for (int i = 0; i < 60; i++) {
            final int slotId = i + 1;
            SlotStatus status = slotStatuses.get(i);
            // èŽ·å–å½“å‰å¡æ§½çš„å®žé™…çŠ¶æ€
            Fkj slotInfo = null;
            if (slotArray != null && i < slotArray.length) {
                slotInfo = slotArray[i];
            }
            SlotStatus currentStatus = i < slotStatuses.size() ? slotStatuses.get(i) : SlotStatus.NO_CARD;
            // åˆ›å»º final å‰¯æœ¬ç”¨äºŽå†…部类
            final SlotStatus finalStatus = currentStatus;
            
            JButton slotButton = new JButton();
            slotButton.setLayout(new BorderLayout());
            slotButton.setBackground(status.getColor());
            slotButton.setBackground(currentStatus.getColor());
            slotButton.setForeground(Color.WHITE);
            slotButton.setFocusPainted(false);
            slotButton.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5));
            
            // æ·»åŠ é¼ æ ‡æ‚¬åœæ•ˆæžœ
            // æ·»åŠ é¼ æ ‡æ‚¬åœæ•ˆæžœ - ä½¿ç”¨ finalStatus è€Œä¸æ˜¯ status
            slotButton.addMouseListener(new java.awt.event.MouseAdapter() {
                public void mouseEntered(java.awt.event.MouseEvent evt) {
                    if (status == SlotStatus.HAS_CARD) {
                        slotButton.setBackground(brighterColor(status.getColor()));
                    if (finalStatus == SlotStatus.HAS_CARD) {
                        slotButton.setBackground(brighterColor(finalStatus.getColor()));
                    }
                }
                
                public void mouseExited(java.awt.event.MouseEvent evt) {
                    slotButton.setBackground(status.getColor());
                    slotButton.setBackground(finalStatus.getColor());
                }
            });
            
@@ -402,14 +479,16 @@
            slotIdLabel.setFont(new Font("Microsoft YaHei", Font.BOLD, 16));
            slotIdLabel.setForeground(Color.WHITE);
            
            // å¡æ§½çŠ¶æ€ - ä¿®æ”¹ï¼šæœ‰å¡æ—¶æ˜¾ç¤ºå¡å·ï¼Œæ— å¡æ—¶æ˜¾ç¤º"无卡"
            // å¡æ§½çŠ¶æ€ - æ ¹æ®å®žé™…状态显示
            String displayText;
            if (status == SlotStatus.HAS_CARD) {
                // ä»ŽSlotManager获取卡号
                Fkj slotInfo = slotManager.getSlotInfo(slotId);
                String cardNumber = slotInfo != null ? slotInfo.getCardNumber() : "-1";
                // æ˜¾ç¤ºå®žé™…卡号
                displayText = cardNumber;
            if (currentStatus == SlotStatus.HAS_CARD && slotInfo != null) {
                String cardNumber = slotInfo.getCardNumber();
                if (cardNumber != null && !cardNumber.trim().isEmpty() &&
                    !"0000".equals(cardNumber) && !"-1".equals(cardNumber)) {
                    displayText = cardNumber;
                } else {
                    displayText = "有卡";
                }
            } else {
                displayText = "无卡";
            }
@@ -447,14 +526,7 @@
                    JLabel label = (JLabel) comp;
                    try {
                        int slotId = Integer.parseInt(label.getText());
                        SlotStatus status = slotStatuses.get(slotId - 1);
                        // å¦‚果是有卡状态,执行取卡操作
                        if (status == SlotStatus.HAS_CARD) {
                            takeCard(slotId);
                        }
                        // å‘送开门指令(不管是否有卡)
                        sendOpenDoorCommand(slotId);
                        Sendmsg.opendoorzhiling(slotId,2);
                        break;
                    } catch (NumberFormatException ex) {
                        // å¿½ç•¥éžæ•°å­—标签
@@ -464,249 +536,23 @@
        }
    }
    
    // æ–°å¢žæ–¹æ³•:发送单个卡槽开门指令
    private void sendOpenDoorCommand(int slotId) {
        try {
            // ç”Ÿæˆå¼€é—¨æŒ‡ä»¤
            String command = OpenDoor.openOneDoor(slotId, OpenDoor.TYPE_ADMIN);
            // å‘送串口指令
            boolean sent = Sendmsg.sendMessage(command);
            if (sent) {
                //System.out.println("成功发送开门指令到卡槽 " + slotId);
            } else {
                System.err.println("发送开门指令到卡槽 " + slotId + " å¤±è´¥");
            }
        } catch (Exception e) {
            System.err.println("生成开门指令失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
    private void takeCard(int slotId) {
        int index = slotId - 1;
        // æ›´æ–°å¡æ§½çŠ¶æ€ä¸ºæ— å¡
        slotStatuses.set(index, SlotStatus.NO_CARD);
        // è°ƒç”¨ SlotManager çš„changgehaska方法更新状态
        SlotManager.changgehaska(slotId, "1"); // "1"表示管理员操作
        updateCardSlotsDisplay();
        updateStatistics();
    }
    // ä¼˜åŒ–的卡槽显示更新
 // ä¿®æ”¹æ›´æ–°å¡æ§½æ˜¾ç¤ºçš„æ–¹æ³•
    private void updateCardSlotsDisplay() {
        for (int i = 0; i < 60; i++) {
            JButton slotButton = slotButtons.get(i);
            SlotStatus status = slotStatuses.get(i);
            // åªæ›´æ–°æ”¹å˜çš„状态
            if (!slotButton.getBackground().equals(status.getColor())) {
                slotButton.setBackground(status.getColor());
            }
            // æ›´æ–°çŠ¶æ€æ ‡ç­¾ - ä¿®æ”¹ï¼šæœ‰å¡æ—¶æ˜¾ç¤ºå¡å·ï¼Œæ— å¡æ—¶æ˜¾ç¤º"无卡"
            Component[] components = slotButton.getComponents();
            for (Component comp : components) {
                if (comp instanceof JLabel) {
                    JLabel label = (JLabel) comp;
                    // æ‰¾åˆ°çŠ¶æ€æ ‡ç­¾ï¼ˆè¾ƒå°çš„å­—ä½“ï¼‰
                    if (label.getFont().getSize() == 11) {
                        String displayText;
                        if (status == SlotStatus.HAS_CARD) {
                            // ä»ŽSlotManager获取卡号
                            Fkj slotInfo = slotManager.getSlotInfo(i + 1);
                            String cardNumber = slotInfo != null ? slotInfo.getCardNumber() : "-1";
                            // æ˜¾ç¤ºå®žé™…卡号
                            displayText = cardNumber;
                        } else {
                            displayText = "无卡";
                        }
                        label.setText(displayText);
                        break;
                    }
                }
            }
        }
        cardSlotsPanel.revalidate();
        cardSlotsPanel.repaint();
    }
    private void openAllSlots() {
        int openedCount = 0;
        // æ›´æ–°æ‰€æœ‰æœ‰å¡å¡æ§½ä¸ºæ— å¡
        for (int i = 0; i < slotStatuses.size(); i++) {
            if (slotStatuses.get(i) == SlotStatus.HAS_CARD) {
                slotStatuses.set(i, SlotStatus.NO_CARD);
                // è°ƒç”¨ SlotManager çš„changgehaska方法更新状态
                SlotManager.changgehaska(i + 1, "1"); // "1"表示管理员操作
                openedCount++;
            }
        }
        if (openedCount > 0) {
            updateCardSlotsDisplay();
            updateStatistics();
        }
        // ä¿®æ”¹ï¼šç›´æŽ¥è°ƒç”¨ä¸²å£å‘送方法,不显示进度对话框
        openAllSlotsWithSerialCommands(openedCount);
    }
    // æ–°å¢žæ–¹æ³•:通过串口发送开门指令
    private void openAllSlotsWithSerialCommands(int openedCount) {
        // ä¿®æ”¹ï¼šç›´æŽ¥è°ƒç”¨OpenDoor的异步开门方法,不显示进度对话框
        OpenDoor.openAllSlotsAsync(60, 250, OpenDoor.TYPE_ADMIN, new OpenDoor.OpenDoorCallback() {
            @Override
            public void onProgress(int currentSlot, int totalSlots, String command) {
                // å‘送串口指令
                boolean sent = Sendmsg.sendMessage(command);
                if (!sent) {
                    System.err.println("发送指令失败: " + command);
                }
            }
            @Override
            public void onComplete(String[] commands) {
                SwingUtilities.invokeLater(() -> {
                    String message;
                    if (openedCount > 0) {
                        message = "已成功打开全部 " + openedCount + " ä¸ªæœ‰å¡å¡æ§½\n发送了 " + commands.length + " æ¡å¼€é—¨æŒ‡ä»¤";
                    } else {
                        message = "已发送 " + commands.length + " æ¡å¼€é—¨æŒ‡ä»¤åˆ°æ‰€æœ‰å¡æ§½";
                    }
                    showResultDialog("success", "操作成功", message);
                });
            }
            @Override
            public void onError(Exception error) {
                SwingUtilities.invokeLater(() -> {
                    showResultDialog("error", "操作失败",
                        "发送开门指令时发生错误: " + error.getMessage());
                    error.printStackTrace();
                });
            }
        });
    private void openAllSlots() {
        Sendmsg.openAllSlots(2);
    }
    
    // ä¼˜åŒ–的统计计算
    private void updateStatistics() {
        int hasCardCount = 0;
        for (SlotStatus status : slotStatuses) {
            if (status == SlotStatus.HAS_CARD) {
        for (int i = 0; i < 60 && i < slotStatuses.size(); i++) {
            if (slotStatuses.get(i) == SlotStatus.HAS_CARD) {
                hasCardCount++;
            }
        }
        
        cardsCountLabel.setText("有卡: " + hasCardCount + "/60");
    }
    // ä¼˜åŒ–的结果对话框显示
    private void showResultDialog(String type, String title, String message) {
        if (resultDialog == null) {
            createResultDialog();
        }
        updateResultDialog(type, title, message);
        resultDialog.setVisible(true);
    }
    private void createResultDialog() {
        resultDialog = new JDialog(this, "", true);
        resultDialog.setSize(400, 250);
        resultDialog.setLocationRelativeTo(this);
        resultDialog.setResizable(false);
        resultDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        
        JPanel contentPanel = new JPanel();
        contentPanel.setLayout(new BorderLayout());
        contentPanel.setBorder(new EmptyBorder(25, 25, 25, 25));
        contentPanel.setBackground(DARK_LIGHT_COLOR);
        // å›¾æ ‡å’Œæ ‡é¢˜
        JPanel headerPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
        headerPanel.setOpaque(false);
        JLabel iconLabel = new JLabel();
        iconLabel.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 32));
        JLabel titleLabel = new JLabel();
        titleLabel.setFont(new Font("Microsoft YaHei", Font.BOLD, 20));
        titleLabel.setForeground(TEXT_COLOR);
        headerPanel.add(iconLabel);
        headerPanel.add(Box.createRigidArea(new Dimension(10, 0)));
        headerPanel.add(titleLabel);
        // æ¶ˆæ¯å†…容
        JLabel messageLabel = new JLabel();
        messageLabel.setFont(new Font("Microsoft YaHei", Font.PLAIN, 14));
        messageLabel.setForeground(TEXT_LIGHT_COLOR);
        messageLabel.setHorizontalAlignment(SwingConstants.CENTER);
        messageLabel.setBorder(new EmptyBorder(15, 0, 25, 0));
        // ç¡®å®šæŒ‰é’®
        JButton confirmButton = new JButton("确定");
        confirmButton.setFont(new Font("Microsoft YaHei", Font.BOLD, 14));
        confirmButton.setBackground(SECONDARY_COLOR);
        confirmButton.setForeground(Color.WHITE);
        confirmButton.setFocusPainted(false);
        confirmButton.setBorder(BorderFactory.createEmptyBorder(10, 30, 10, 30));
        confirmButton.addActionListener(e -> resultDialog.dispose());
        // æ·»åŠ æ‚¬åœæ•ˆæžœ
        confirmButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseEntered(java.awt.event.MouseEvent evt) {
                confirmButton.setBackground(brighterColor(SECONDARY_COLOR));
            }
            public void mouseExited(java.awt.event.MouseEvent evt) {
                confirmButton.setBackground(SECONDARY_COLOR);
            }
        });
        JPanel buttonPanel = new JPanel();
        buttonPanel.setOpaque(false);
        buttonPanel.add(confirmButton);
        contentPanel.add(headerPanel, BorderLayout.NORTH);
        contentPanel.add(messageLabel, BorderLayout.CENTER);
        contentPanel.add(buttonPanel, BorderLayout.SOUTH);
        resultDialog.add(contentPanel);
    }
    private void updateResultDialog(String type, String title, String message) {
        resultDialog.setTitle(title);
        Component[] components = ((JPanel)resultDialog.getContentPane().getComponent(0)).getComponents();
        // æ›´æ–°å›¾æ ‡å’Œæ ‡é¢˜
        JPanel headerPanel = (JPanel) components[0];
        JLabel iconLabel = (JLabel) headerPanel.getComponent(0);
        JLabel titleLabel = (JLabel) headerPanel.getComponent(2);
        if ("success".equals(type)) {
            iconLabel.setText("✅");
        } else if ("warning".equals(type)) {
            iconLabel.setText("⚠️");
        } else if ("error".equals(type)) {
            iconLabel.setText("❌");
        } else {
            iconLabel.setText("ℹ️");
        }
        titleLabel.setText(title);
        // æ›´æ–°æ¶ˆæ¯
        JLabel messageLabel = (JLabel) components[1];
        messageLabel.setText("<html><div style='text-align: center;'>" + message.replace("\n", "<br>") + "</div></html>");
        // è°ƒè¯•统计信息
        //System.out.println("更新统计信息: æœ‰å¡ " + hasCardCount + "/60");
    }
    
    private Color brighterColor(Color color) {
@@ -756,19 +602,10 @@
        if (slotStatuses != null) {
            slotStatuses.clear();
        }
        if (progressDialog != null) {
            progressDialog.dispose();
            progressDialog = null;
        }
        if (resultDialog != null) {
            resultDialog.dispose();
            resultDialog = null;
        }
        // æ¢å¤è½®è¯¢çŠ¶æ€
        resumePollingWhenExiting();
        
        super.dispose();
        //System.out.println("快速取卡页面已关闭");
    }
    
    // é™æ€æ–¹æ³•:从系统设置页面调用
src/xitongshezhi/lishijilu.java
@@ -17,20 +17,27 @@
    private static final Color DARK_LIGHT_COLOR = new Color(26, 43, 68);
    private static final Color TEXT_COLOR = new Color(224, 224, 224);
    private static final Color TEXT_LIGHT_COLOR = new Color(160, 200, 255);
    private static final Color DELETE_COLOR = new Color(231, 76, 60); // åˆ é™¤æŒ‰é’®é¢œè‰²
    private static final Color ACTIVE_BUTTON_COLOR = new Color(41, 128, 185); // æ¿€æ´»æŒ‰é’®é¢œè‰²
    
    private JPanel mainPanel;
    private JLabel titleLabel;
    private JButton logButton; // æ—¥å¿—记录按钮
    private JButton errorLogButton; // é”™è¯¯æ—¥å¿—按钮
    private JButton backButton;
    private JButton deleteAllButton;
    
    // æ–‡æœ¬åŸŸç»„ä»¶
    private JScrollPane textScrollPane;
    private JTextArea contentTextArea;
    
    // å½“前显示的日志类型
    private String currentLogType = "log"; // "log" æˆ– "error"
    public lishijilu(JFrame parent) {
        super(parent, "", true);
        initializeUI();
        setupEventListeners();
        loadLogContent(); // åŠ è½½æ—¥å¿—å†…å®¹
        loadLogContent(); // é»˜è®¤åŠ è½½æ“ä½œæ—¥å¿—
    }
    private void initializeUI() {
@@ -71,12 +78,48 @@
        headerPanel.setOpaque(false);
        headerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 12, 0));
        
        // æ ‡é¢˜
        titleLabel = new JLabel("历史记录");
        titleLabel.setFont(new Font("Microsoft YaHei", Font.BOLD, 22));
        titleLabel.setForeground(TEXT_COLOR);
        // åˆ›å»ºæ—¥å¿—类型选择按钮面板
        JPanel logTypePanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0));
        logTypePanel.setOpaque(false);
        
        // è¿”回按钮 - ä½¿ç”¨ä¸é€æ˜Žè®¾è®¡
        // æ—¥å¿—记录按钮
        logButton = new JButton("日志记录");
        logButton.setFont(new Font("Microsoft YaHei", Font.PLAIN, 14));
        logButton.setBackground(ACTIVE_BUTTON_COLOR); // é»˜è®¤æ¿€æ´»çŠ¶æ€
        logButton.setForeground(Color.WHITE);
        logButton.setOpaque(true);
        logButton.setFocusPainted(false);
        logButton.setBorder(BorderFactory.createEmptyBorder(8, 16, 8, 16));
        logButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
        // é”™è¯¯æ—¥å¿—按钮
        errorLogButton = new JButton("错误日志");
        errorLogButton.setFont(new Font("Microsoft YaHei", Font.PLAIN, 14));
        errorLogButton.setBackground(PRIMARY_COLOR); // é»˜è®¤éžæ¿€æ´»çŠ¶æ€
        errorLogButton.setForeground(Color.WHITE);
        errorLogButton.setOpaque(true);
        errorLogButton.setFocusPainted(false);
        errorLogButton.setBorder(BorderFactory.createEmptyBorder(8, 16, 8, 16));
        errorLogButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
        logTypePanel.add(logButton);
        logTypePanel.add(errorLogButton);
        // æ“ä½œæŒ‰é’®é¢æ¿
        JPanel actionButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0));
        actionButtonPanel.setOpaque(false);
        // åˆ é™¤å…¨éƒ¨è®°å½•按钮
        deleteAllButton = new JButton("删除全部");
        deleteAllButton.setFont(new Font("Microsoft YaHei", Font.PLAIN, 14));
        deleteAllButton.setBackground(DELETE_COLOR);
        deleteAllButton.setForeground(Color.WHITE);
        deleteAllButton.setOpaque(true);
        deleteAllButton.setFocusPainted(false);
        deleteAllButton.setBorder(BorderFactory.createEmptyBorder(8, 16, 8, 16));
        deleteAllButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
        // è¿”回按钮
        backButton = new JButton("关闭");
        backButton.setFont(new Font("Microsoft YaHei", Font.PLAIN, 14));
        backButton.setBackground(PRIMARY_COLOR);
@@ -85,6 +128,60 @@
        backButton.setFocusPainted(false);
        backButton.setBorder(BorderFactory.createEmptyBorder(8, 16, 8, 16));
        backButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
        actionButtonPanel.add(deleteAllButton);
        actionButtonPanel.add(backButton);
        // æŒ‰é’®æ‚¬åœæ•ˆæžœ
        setupButtonHoverEffects();
        headerPanel.add(logTypePanel, BorderLayout.WEST);
        headerPanel.add(actionButtonPanel, BorderLayout.EAST);
        return headerPanel;
    }
    private void setupButtonHoverEffects() {
        // æ—¥å¿—按钮悬停效果
        logButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseEntered(java.awt.event.MouseEvent evt) {
                if (!currentLogType.equals("log")) {
                    logButton.setBackground(brighterColor(PRIMARY_COLOR));
                }
            }
            public void mouseExited(java.awt.event.MouseEvent evt) {
                if (!currentLogType.equals("log")) {
                    logButton.setBackground(PRIMARY_COLOR);
                }
            }
        });
        // é”™è¯¯æ—¥å¿—按钮悬停效果
        errorLogButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseEntered(java.awt.event.MouseEvent evt) {
                if (!currentLogType.equals("error")) {
                    errorLogButton.setBackground(brighterColor(PRIMARY_COLOR));
                }
            }
            public void mouseExited(java.awt.event.MouseEvent evt) {
                if (!currentLogType.equals("error")) {
                    errorLogButton.setBackground(PRIMARY_COLOR);
                }
            }
        });
        // åˆ é™¤æŒ‰é’®æ‚¬åœæ•ˆæžœ
        deleteAllButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseEntered(java.awt.event.MouseEvent evt) {
                deleteAllButton.setBackground(brighterColor(DELETE_COLOR));
            }
            public void mouseExited(java.awt.event.MouseEvent evt) {
                deleteAllButton.setBackground(DELETE_COLOR);
            }
        });
        
        // è¿”回按钮悬停效果
        backButton.addMouseListener(new java.awt.event.MouseAdapter() {
@@ -96,19 +193,6 @@
                backButton.setBackground(PRIMARY_COLOR);
            }
        });
        JPanel titlePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        titlePanel.setOpaque(false);
        titlePanel.add(titleLabel);
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        buttonPanel.setOpaque(false);
        buttonPanel.add(backButton);
        headerPanel.add(titlePanel, BorderLayout.WEST);
        headerPanel.add(buttonPanel, BorderLayout.EAST);
        return headerPanel;
    }
    
    private JPanel createContentPanel() {
@@ -177,37 +261,138 @@
        backButton.addActionListener(e -> {
            dispose();
        });
        // åˆ é™¤å…¨éƒ¨è®°å½•按钮
        deleteAllButton.addActionListener(e -> {
            deleteAllRecords();
        });
        // æ—¥å¿—记录按钮
        logButton.addActionListener(e -> {
            switchToLogType("log");
        });
        // é”™è¯¯æ—¥å¿—按钮
        errorLogButton.addActionListener(e -> {
            switchToLogType("error");
        });
    }
    // åˆ‡æ¢æ—¥å¿—类型
    private void switchToLogType(String logType) {
        if (currentLogType.equals(logType)) {
            return; // å·²ç»æ˜¯å½“前类型,无需切换
        }
        currentLogType = logType;
        // æ›´æ–°æŒ‰é’®çŠ¶æ€
        if (logType.equals("log")) {
            logButton.setBackground(ACTIVE_BUTTON_COLOR);
            errorLogButton.setBackground(PRIMARY_COLOR);
        } else {
            logButton.setBackground(PRIMARY_COLOR);
            errorLogButton.setBackground(ACTIVE_BUTTON_COLOR);
        }
        // åŠ è½½å¯¹åº”ç±»åž‹çš„æ—¥å¿—å†…å®¹
        loadLogContent();
    }
    // åˆ é™¤å…¨éƒ¨è®°å½•的方法
    private void deleteAllRecords() {
        // ç¡®è®¤å¯¹è¯æ¡†
        String logTypeName = currentLogType.equals("log") ? "操作" : "错误";
        int result = JOptionPane.showConfirmDialog(
            this,
            "确定要删除所有" + logTypeName + "记录吗?此操作不可恢复!",
            "确认删除",
            JOptionPane.YES_NO_OPTION,
            JOptionPane.WARNING_MESSAGE
        );
        if (result == JOptionPane.YES_OPTION) {
            try {
                String fileName = currentLogType.equals("log") ? "log.properties" : "err.properties";
                File logFile = new File(fileName);
                if (logFile.exists()) {
                    // åˆ›å»ºç©ºçš„Properties对象并写入文件
                    Properties emptyProps = new Properties();
                    try (FileOutputStream out = new FileOutputStream(logFile);
                         OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8")) {
                        emptyProps.store(writer, "发卡机" + logTypeName + "记录 - æ‰€æœ‰è®°å½•已清空");
                    }
                    // æ›´æ–°æ–‡æœ¬åŸŸæ˜¾ç¤º
                    contentTextArea.setText("所有" + logTypeName + "记录已成功删除。\n\n日志文件已清空。");
                    // æ˜¾ç¤ºæˆåŠŸæ¶ˆæ¯
                    JOptionPane.showMessageDialog(
                        this,
                        "所有" + logTypeName + "记录已成功删除。",
                        "删除成功",
                        JOptionPane.INFORMATION_MESSAGE
                    );
                } else {
                    contentTextArea.setText(logTypeName + "日志文件不存在,无需删除。");
                }
            } catch (IOException ex) {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(
                    this,
                    "删除记录时出错: " + ex.getMessage(),
                    "错误",
                    JOptionPane.ERROR_MESSAGE
                );
            }
        }
    }
    
    // åŠ è½½æ—¥å¿—å†…å®¹
    private void loadLogContent() {
        File logFile = new File("log.properties");
        String fileName = currentLogType.equals("log") ? "log.properties" : "err.properties";
        String logTypeName = currentLogType.equals("log") ? "操作" : "错误";
        File logFile = new File(fileName);
        
        if (!logFile.exists()) {
            contentTextArea.setText("日志文件不存在。");
            contentTextArea.setText(logTypeName + "日志文件不存在。");
            return;
        }
        
        Properties logProps = new Properties();
        try (FileInputStream in = new FileInputStream(logFile)) {
            logProps.load(in);
        try (FileInputStream in = new FileInputStream(logFile);
             InputStreamReader reader = new InputStreamReader(in, "UTF-8")) {
            logProps.load(reader);
            
            // æ£€æŸ¥è®°å½•数量,如果超过1000条则删除旧记录
            if (logProps.size() > 1000) {
                trimLogProperties(logProps);
                trimLogProperties(logProps, fileName);
            }
            
            // æž„建显示内容
            StringBuilder content = new StringBuilder();
            content.append("日志文件内容 (").append(logProps.size()).append(" æ¡è®°å½•):\n\n");
            content.append(logTypeName).append("日志内容 (").append(logProps.size()).append(" æ¡è®°å½•):\n\n");
            
            // æŒ‰æ—¶é—´æˆ³æŽ’序显示
            logProps.stringPropertyNames().stream()
                .sorted((a, b) -> Long.compare(Long.parseLong(b), Long.parseLong(a)))
                .sorted((a, b) -> {
                    try {
                        // ä»Žé”®ä¸­æå–时间戳部分进行比较
                        long timeA = extractTimestampFromKey(a);
                        long timeB = extractTimestampFromKey(b);
                        return Long.compare(timeB, timeA); // é™åºæŽ’列,最新的在前
                    } catch (Exception e) {
                        return b.compareTo(a); // å¦‚果提取失败,使用字符串比较
                    }
                })
                .forEach(key -> {
                    String value = logProps.getProperty(key);
                    content.append("时间戳: ").append(key).append("\n");
                    content.append("内容: ").append(value).append("\n");
                    content.append("时间: ").append(extractTimeFromValue(value)).append("\n");
                    content.append("内容: ").append(extractOperationFromValue(value)).append("\n");
                    content.append("----------------------------------------\n");
                });
            
@@ -215,23 +400,71 @@
            
        } catch (IOException e) {
            e.printStackTrace();
            contentTextArea.setText("加载日志文件时出错: " + e.getMessage());
        } catch (NumberFormatException e) {
            contentTextArea.setText("日志文件格式错误。");
            contentTextArea.setText("加载" + logTypeName + "日志文件时出错: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            contentTextArea.setText("处理" + logTypeName + "日志内容时出错: " + e.getMessage());
        }
    }
    
    // ä»Žé”®ä¸­æå–时间戳
    private long extractTimestampFromKey(String key) {
        try {
            // é”®çš„æ ¼å¼: log_时间戳_UUID æˆ– error_时间戳_UUID
            String[] parts = key.split("_");
            if (parts.length >= 2) {
                return Long.parseLong(parts[1]);
            }
        } catch (Exception e) {
            // å¦‚果解析失败,返回0
        }
        return 0L;
    }
    // ä»Žå€¼ä¸­æå–时间部分
    private String extractTimeFromValue(String value) {
        if (value == null) return "未知时间";
        // å€¼çš„æ ¼å¼: [2025-11-21 14:44:39] å–卡操作:卡槽19被管理员取卡
        int start = value.indexOf('[');
        int end = value.indexOf(']');
        if (start >= 0 && end > start) {
            return value.substring(start + 1, end);
        }
        return value;
    }
    // ä»Žå€¼ä¸­æå–操作部分
    private String extractOperationFromValue(String value) {
        if (value == null) return "未知内容";
        // å€¼çš„æ ¼å¼: [2025-11-21 14:44:39] å–卡操作:卡槽19被管理员取卡
        int end = value.indexOf(']');
        if (end >= 0 && end + 1 < value.length()) {
            return value.substring(end + 1).trim();
        }
        return value;
    }
    // ä¿®å‰ªæ—¥å¿—属性,只保留最新的1000条记录
    private void trimLogProperties(Properties logProps) {
    private void trimLogProperties(Properties logProps, String fileName) {
        // æŒ‰æ—¶é—´æˆ³æŽ’序,保留最新的1000条
        logProps.stringPropertyNames().stream()
            .sorted((a, b) -> Long.compare(Long.parseLong(b), Long.parseLong(a)))
            .sorted((a, b) -> {
                try {
                    long timeA = extractTimestampFromKey(a);
                    long timeB = extractTimestampFromKey(b);
                    return Long.compare(timeB, timeA);
                } catch (Exception e) {
                    return b.compareTo(a);
                }
            })
            .skip(1000)
            .forEach(logProps::remove);
        
        // ä¿å­˜ä¿®å‰ªåŽçš„属性
        try (FileOutputStream out = new FileOutputStream("log.properties")) {
            logProps.store(out, "UWB人员定位卡发卡机历史记录 - è‡ªåŠ¨ä¿®å‰ªè‡³1000条记录");
        try (FileOutputStream out = new FileOutputStream(fileName);
             OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8")) {
            String logTypeName = fileName.equals("log.properties") ? "操作" : "错误";
            logProps.store(writer, "发卡机" + logTypeName + "记录 - è‡ªåŠ¨ä¿®å‰ªè‡³1000条记录");
        } catch (IOException e) {
            e.printStackTrace();
        }
@@ -252,5 +485,4 @@
            dialog.setVisible(true);
        });
    }    
}