张世豪
2025-11-28 7acfc864d11de1fc41cabc2a5d4fad3894c2e5b0
src/chuankou/Sendmsg.java
@@ -1,189 +1,363 @@
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.SlotManager;
import chushihua.lunxun;
import dialog.Charulog;
import dialog.Dingshidialog;
import dialog.Errlog;
import publicway.OpenDoor;
import publicway.TimestampUtil;
import xitongshezhi.SystemDebugDialog;
/**
 * 串口消息发送工具类
 * 提供高性能的串口消息发送功能,适合高频调用
 * 优化内存使用,避免长时间运行内存泄漏
 */
public class Sendmsg {
    // 静态串口服务实例
    private static volatile SerialPortService serialService = null;
    private static volatile boolean isPortOpen = false;
    // 改为非final,支持动态控制
    private static boolean DEBUG_MODE = false;
    // 日期格式化
    private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS");
    /**
     * 设置串口服务实例
     * @param service 串口服务实例
     * @param open 串口是否打开
     */
    public static void setSerialService(SerialPortService service, boolean open) {
        serialService = service;
        isPortOpen = open;
    }
    /**
     * 获取串口服务实例
     */
    public static SerialPortService getSerialService() {
        return serialService;
    }
    /**
     * 发送消息到串口(带重试机制)
     */
    public static boolean sendMessage(String message) {
        if (!isPortOpen || serialService == null) {
            System.err.println("[" + getCurrentTime() + "] 串口未打开,无法发送数据");
            return false;
        }
        if (message == null || message.trim().isEmpty()) {
            System.err.println("[" + getCurrentTime() + "] 发送数据为空");
            return false;
        }
        String text = message.trim();
        int retryCount = 0;
        final int MAX_RETRY = 2;
        while (retryCount <= MAX_RETRY) {
            try {
                byte[] data = hexStringToByteArray(text);
                if (data == null) {
                    System.err.println("[" + getCurrentTime() + "] HEX转换失败,数据: " + text);
                    return false;
                }
                boolean sendResult = serialService.send(data);
                if (sendResult) {
                    if (DEBUG_MODE) {
                        System.out.println("[" + getCurrentTime() + "] 发送成功: " + text.toUpperCase());
                    }
                    return true;
                } else {
                    retryCount++;
                    if (retryCount <= MAX_RETRY) {
                        System.err.println("[" + getCurrentTime() + "] 发送失败,正在重试 (" + retryCount + "/" + MAX_RETRY + ")");
                        try {
                            Thread.sleep(50); // 重试前等待
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            break;
                        }
                    } else {
                        System.err.println("[" + getCurrentTime() + "] 串口发送失败,指令: " + text.toUpperCase());
                        System.err.println("[" + getCurrentTime() + "] 串口状态 - 打开: " + isPortOpen + ", 服务: " + (serialService != null));
                        if (serialService != null) {
                            System.err.println("[" + getCurrentTime() + "] 串口服务状态 - 是否打开: " + serialService.isOpen());
                        }
                        return false;
                    }
                }
            } catch (Exception e) {
                System.err.println("[" + getCurrentTime() + "] 发送异常,指令: " + text.toUpperCase());
                e.printStackTrace();
                return false;
            }
        }
        return false;
    }
    /**
     * 检查串口是否已打开
     * @return 串口打开状态
     */
    public static boolean isPortOpen() {
        boolean open = isPortOpen && serialService != null;
        if (!open && DEBUG_MODE) {
            System.err.println("[" + getCurrentTime() + "] 串口状态检查: 未打开");
        }
        return open;
    }
    /**
     * HEX字符串转字节数组
     * @param s HEX字符串
     * @return 字节数组
     */
    private static byte[] hexStringToByteArray(String s) {
        if (s == null || s.isEmpty()) {
            return new byte[0];
        }
        s = s.replaceAll("\\s+", ""); // 移除空格
        int len = s.length();
        if (len % 2 != 0) {
            throw new IllegalArgumentException("HEX字符串长度必须为偶数,当前长度: " + len + ", 数据: " + s);
        }
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            int high = Character.digit(s.charAt(i), 16);
            int low = Character.digit(s.charAt(i + 1), 16);
            if (high == -1 || low == -1) {
                throw new IllegalArgumentException("无效的HEX字符: '" + s.charAt(i) + s.charAt(i + 1) + "' 在位置 " + i + "-" + (i+1) + ", 完整数据: " + s);
            }
            data[i / 2] = (byte) ((high << 4) + low);
        }
        return data;
    }
    /**
     * 字节数组转HEX字符串
     * @param bytes 字节数组
     * @return HEX字符串
     */
    public static String bytesToHex(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
    /**
     * 获取当前时间字符串
     * @return 时间字符串
     */
    private static String getCurrentTime() {
        return timeFormat.format(new Date());
    }
    /**
     * 启用调试模式
     */
    public static void enableDebugMode() {
        DEBUG_MODE = true;
        System.out.println("[" + getCurrentTime() + "] Sendmsg调试模式已启用");
    }
    /**
     * 禁用调试模式
     */
    public static void disableDebugMode() {
        DEBUG_MODE = false;
        System.out.println("[" + getCurrentTime() + "] Sendmsg调试模式已禁用");
    }
    /**
     * 设置调试模式
     */
    public static void setDebugMode(boolean debug) {
        DEBUG_MODE = debug;
        System.out.println("[" + getCurrentTime() + "] Sendmsg调试模式: " + (debug ? "启用" : "禁用"));
    }
   // 静态串口服务实例
   private static volatile SerialPortService serialService = null;
   private static final AtomicBoolean isPortOpen = new AtomicBoolean(false);
   // 改为非final,支持动态控制
   private static volatile boolean DEBUG_MODE = false;
   // 使用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();
         // 获取当前时间
         String currentTime = TimestampUtil.getTimestamp();
         // 更新卡槽的收到发卡指令时间
         SlotManager.slotArray[slotId-1].setReceiveCardCommandTime(currentTime);
         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 + " 失败");
                  }else {
                     // 获取当前时间
                     String currentTime = TimestampUtil.getTimestamp();
                     // 更新卡槽的收到发卡指令时间
                     SlotManager.slotArray[slotId-1].setReceiveCardCommandTime(currentTime);
                  }
                  // 间隔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();
   }
   /**
    * 设置串口服务实例
    * @param service 串口服务实例
    * @param open 串口是否打开
    */
   public static void setSerialService(SerialPortService service, boolean open) {
      serialService = service;
      isPortOpen.set(open);
   }
   /**
    * 获取串口服务实例
    */
   public static SerialPortService getSerialService() {
      return serialService;
   }
   /**
    * 发送消息到串口(带重试机制)
    */
   public static boolean sendMessage(String message) {
      if (!isPortOpen.get() || serialService == null) {
         Errlog.logOperation("[" + getCurrentTime() + "] 串口未打开,无法发送数据");
         return false;
      }
      if (message == null || message.trim().isEmpty()) {
         Errlog.logOperation("[" + getCurrentTime() + "] 发送数据为空");
         return false;
      }
      String text = message.trim();
      int retryCount = 0;
      final int MAX_RETRY = 2;
      while (retryCount <= MAX_RETRY) {
         try {
            byte[] data = hexStringToByteArray(text);
            if (data == null) {
               Errlog.logOperation("[" + getCurrentTime() + "] HEX转换失败,数据: " + text);
               return false;
            }
            boolean sendResult = serialService.send(data);
            if (sendResult) {
               if (DEBUG_MODE) {
                  System.out.println("[" + getCurrentTime() + "] 发送成功: " + text.toUpperCase());
               }
               return true;
            } else {
               retryCount++;
               if (retryCount <= MAX_RETRY) {
                  Errlog.logOperation("[" + getCurrentTime() + "] 发送失败,正在重试 (" + retryCount + "/" + MAX_RETRY + ")");
                  try {
                     Thread.sleep(50); // 重试前等待
                  } catch (InterruptedException e) {
                     Thread.currentThread().interrupt();
                     break;
                  }
               } else {
                  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) {
            Errlog.logOperation("[" + getCurrentTime() + "] 发送异常,指令: " + text.toUpperCase());
            if (DEBUG_MODE) {
               e.printStackTrace();
            }
            return false;
         }
      }
      return false;
   }
   /**
    * 检查串口是否已打开
    * @return 串口打开状态
    */
   public static boolean isPortOpen() {
      boolean open = isPortOpen.get() && serialService != null;
      if (!open && DEBUG_MODE) {
         Errlog.logOperation("[" + getCurrentTime() + "] 串口状态检查: 未打开");
      }
      return open;
   }
   /**
    * HEX字符串转字节数组
    * @param s HEX字符串
    * @return 字节数组
    */
   private static byte[] hexStringToByteArray(String s) {
      if (s == null || s.isEmpty()) {
         return new byte[0];
      }
      s = s.replaceAll("\\s+", ""); // 移除空格
      int len = s.length();
      if (len % 2 != 0) {
         throw new IllegalArgumentException("HEX字符串长度必须为偶数,当前长度: " + len + ", 数据: " + s);
      }
      byte[] data = new byte[len / 2];
      for (int i = 0; i < len; i += 2) {
         int high = Character.digit(s.charAt(i), 16);
         int low = Character.digit(s.charAt(i + 1), 16);
         if (high == -1 || low == -1) {
            throw new IllegalArgumentException("无效的HEX字符: '" + s.charAt(i) + s.charAt(i + 1) + "' 在位置 " + i + "-" + (i+1) + ", 完整数据: " + s);
         }
         data[i / 2] = (byte) ((high << 4) + low);
      }
      return data;
   }
   /**
    * 字节数组转HEX字符串
    * @param bytes 字节数组
    * @return HEX字符串
    */
   public static String bytesToHex(byte[] bytes) {
      if (bytes == null || bytes.length == 0) {
         return "";
      }
      StringBuilder sb = STRING_BUILDER_CACHE.get();
      sb.setLength(0);
      for (byte b : bytes) {
         sb.append(String.format("%02x", b));
      }
      return sb.toString();
   }
   /**
    * 获取当前时间字符串
    * @return 时间字符串
    */
   private static String getCurrentTime() {
      return TIME_FORMATTER.get().format(new Date());
   }
   /**
    * 启用调试模式
    */
   public static void enableDebugMode() {
      DEBUG_MODE = true;
      System.out.println("[" + getCurrentTime() + "] Sendmsg调试模式已启用");
   }
   /**
    * 禁用调试模式
    */
   public static void disableDebugMode() {
      DEBUG_MODE = false;
      System.out.println("[" + getCurrentTime() + "] Sendmsg调试模式已禁用");
   }
   /**
    * 设置调试模式
    */
   public static void setDebugMode(boolean debug) {
      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();
   }
}