package chuankou; import javax.swing.*; import javax.swing.border.EmptyBorder; import java.awt.*; import java.text.SimpleDateFormat; import java.util.Date; import chushihua.lunxun; public class SystemDebugDialog extends JDialog { private static final long serialVersionUID = -9131186661220052051L; // 屏幕尺寸常量 - 适配7寸竖屏 private static final int DIALOG_WIDTH = 580; private static final int DIALOG_HEIGHT = 900; // 颜色常量 private static final Color PRIMARY_COLOR = new Color(52, 152, 219); 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 TEXT_COLOR = new Color(224, 224, 224); // UI组件 private JTextArea dataTextArea; private JButton clearButton; private JButton pollingButton; private JButton scrollButton; private JTextField sendTextField; private JButton sendButton; // 静态变量来支持外部类调用和静态显示控制 private static volatile boolean staticDataUpdateEnabled = true; private static JTextArea staticDataTextArea; private static SimpleDateFormat staticTimeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); // 串口服务 private SerialPortService serialService; // 控制变量 private boolean autoScroll = true; // 默认自动滚动 private static boolean dataUpdateEnabled = true; // 控制数据更新 // 日期格式化 private SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); // 新增:内存优化相关变量 private static final int MAX_DISPLAY_LINES = 500; // 最大显示行数 private static final int TRIM_THRESHOLD = 600; // 修剪阈值 private static final int MAX_LINE_LENGTH = 200; // 单行最大长度 private static final long MEMORY_CHECK_INTERVAL = 30000; // 内存检查间隔30秒 private Timer memoryMonitorTimer; public SystemDebugDialog(JFrame parent) { super(parent, "系统调试 - 串口调试工具", true); initializeUI(); setupSerialDataCapture(); startMemoryMonitoring(); // 启动内存监控 } private void initializeUI() { setSize(DIALOG_WIDTH, DIALOG_HEIGHT); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setLocationRelativeTo(null); setResizable(false); // 设置深色主题 try { UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); } catch (Exception e) { e.printStackTrace(); } // 创建主面板 JPanel mainPanel = new JPanel(); mainPanel.setLayout(new BorderLayout()); mainPanel.setBackground(DARK_COLOR); // 不透明背景 mainPanel.setBorder(new EmptyBorder(15, 15, 15, 15)); // 添加各个区域 mainPanel.add(createHeaderPanel(), BorderLayout.NORTH); mainPanel.add(createControlPanel(), BorderLayout.CENTER); mainPanel.add(createSendPanel(), BorderLayout.SOUTH); getContentPane().add(mainPanel); // 初始化静态引用,供静态方法使用 staticDataTextArea = dataTextArea; } private JPanel createHeaderPanel() { JPanel headerPanel = new JPanel(new BorderLayout()); headerPanel.setOpaque(false); headerPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, new Color(52, 152, 219, 77))); // 标题 JLabel titleLabel = new JLabel("系统调试 - 串口调试"); titleLabel.setFont(new Font("Microsoft YaHei", Font.BOLD, 20)); titleLabel.setForeground(TEXT_COLOR); titleLabel.setIcon(createIcon("🛠️", 24)); // 关闭按钮 JButton closeButton = new JButton("关闭"); closeButton.setFont(new Font("Microsoft YaHei", Font.PLAIN, 14)); closeButton.setBackground(DANGER_COLOR); closeButton.setForeground(Color.WHITE); closeButton.setFocusPainted(false); closeButton.setBorder(BorderFactory.createEmptyBorder(8, 16, 8, 16)); closeButton.addActionListener(e -> { dispose(); }); // 添加悬停效果 closeButton.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseEntered(java.awt.event.MouseEvent evt) { closeButton.setBackground(brighterColor(DANGER_COLOR)); } public void mouseExited(java.awt.event.MouseEvent evt) { closeButton.setBackground(DANGER_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(closeButton); headerPanel.add(titlePanel, BorderLayout.WEST); headerPanel.add(buttonPanel, BorderLayout.EAST); // 添加装饰线 JPanel decorLine = new JPanel(); decorLine.setBackground(PRIMARY_COLOR); decorLine.setPreferredSize(new Dimension(120, 3)); JPanel headerWrapper = new JPanel(new BorderLayout()); headerWrapper.setOpaque(false); headerWrapper.add(headerPanel, BorderLayout.CENTER); headerWrapper.add(decorLine, BorderLayout.SOUTH); return headerWrapper; } private JPanel createControlPanel() { JPanel controlPanel = new JPanel(); controlPanel.setLayout(new BorderLayout()); controlPanel.setOpaque(false); controlPanel.setBorder(new EmptyBorder(20, 0, 20, 0)); // 数据显示区域 controlPanel.add(createDataDisplayPanel(), BorderLayout.CENTER); // 控制按钮区域 controlPanel.add(createButtonPanel(), BorderLayout.SOUTH); return controlPanel; } private JPanel createDataDisplayPanel() { JPanel displayPanel = new JPanel(new BorderLayout()); displayPanel.setOpaque(false); displayPanel.setBorder(new EmptyBorder(0, 0, 15, 0)); // 标题标签 JLabel titleLabel = new JLabel("实时数据"); titleLabel.setFont(new Font("Microsoft YaHei", Font.BOLD, 14)); titleLabel.setForeground(TEXT_COLOR); titleLabel.setBorder(new EmptyBorder(0, 0, 8, 0)); displayPanel.add(titleLabel, BorderLayout.NORTH); dataTextArea = new JTextArea(); dataTextArea.setFont(new Font("Consolas", Font.PLAIN, 12)); dataTextArea.setForeground(TEXT_COLOR); dataTextArea.setBackground(new Color(30, 30, 30)); // 不透明背景 dataTextArea.setEditable(false); dataTextArea.setLineWrap(true); dataTextArea.setWrapStyleWord(true); // 滚动面板 JScrollPane scrollPane = new JScrollPane(dataTextArea); scrollPane.setBorder(BorderFactory.createLineBorder(new Color(100, 100, 100))); scrollPane.getVerticalScrollBar().setUnitIncrement(16); scrollPane.setOpaque(false); scrollPane.getViewport().setOpaque(false); displayPanel.add(scrollPane, BorderLayout.CENTER); return displayPanel; } private JPanel createButtonPanel() { JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 0)); buttonPanel.setOpaque(false); buttonPanel.setBorder(new EmptyBorder(10, 0, 0, 0)); // 开始/暂停滚动按钮 scrollButton = new JButton("暂停"); scrollButton.setFont(new Font("Microsoft YaHei", Font.BOLD, 14)); scrollButton.setBackground(PRIMARY_COLOR); scrollButton.setForeground(Color.WHITE); scrollButton.setFocusPainted(false); 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("清空数据"); clearButton.setFont(new Font("Microsoft YaHei", Font.BOLD, 14)); clearButton.setBackground(WARNING_COLOR); clearButton.setForeground(Color.WHITE); clearButton.setFocusPainted(false); clearButton.setBorder(BorderFactory.createEmptyBorder(8, 20, 8, 20)); clearButton.addActionListener(e -> clearData()); buttonPanel.add(scrollButton); buttonPanel.add(pollingButton); buttonPanel.add(clearButton); return buttonPanel; } private JPanel createSendPanel() { JPanel sendPanel = new JPanel(new BorderLayout()); sendPanel.setOpaque(false); // 标题标签 JLabel titleLabel = new JLabel("发送数据 (HEX格式)"); titleLabel.setFont(new Font("Microsoft YaHei", Font.BOLD, 14)); titleLabel.setForeground(TEXT_COLOR); titleLabel.setBorder(new EmptyBorder(0, 0, 8, 0)); sendPanel.add(titleLabel, BorderLayout.NORTH); JPanel inputPanel = new JPanel(new BorderLayout(10, 0)); inputPanel.setOpaque(false); sendTextField = new JTextField(); sendTextField.setFont(new Font("Consolas", Font.PLAIN, 14)); sendTextField.setBackground(new Color(255, 255, 255)); // 不透明背景 sendTextField.setForeground(Color.BLACK); sendTextField.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(new Color(100, 100, 100)), BorderFactory.createEmptyBorder(8, 8, 8, 8) )); sendButton = new JButton("发送"); sendButton.setFont(new Font("Microsoft YaHei", Font.BOLD, 14)); sendButton.setBackground(SECONDARY_COLOR); sendButton.setForeground(Color.WHITE); sendButton.setFocusPainted(false); sendButton.setBorder(BorderFactory.createEmptyBorder(8, 20, 8, 20)); sendButton.addActionListener(e -> sendData()); // 添加回车键发送支持 sendTextField.addActionListener(e -> sendData()); inputPanel.add(sendTextField, BorderLayout.CENTER); inputPanel.add(sendButton, BorderLayout.EAST); sendPanel.add(inputPanel, BorderLayout.CENTER); return sendPanel; } private void setupSerialDataCapture() { try { // 获取Sendmsg中设置的串口服务 serialService = Sendmsg.getSerialService(); if (serialService != null && serialService.isOpen()) { // 使用实例方法而不是静态方法引用 serialService.startCapture(this::onDataReceivedHEX); appendAsciiData("[" + getCurrentTime() + "] Serial data capture started\n"); // 确保串口服务没有暂停 serialService.setPaused(false); } else { appendAsciiData("[" + getCurrentTime() + "] Warning: Serial port not connected, unable to capture data\n"); } } catch (Exception e) { appendAsciiData("[" + getCurrentTime() + "] Error: Failed to set up serial data capture" + e.getMessage() + "\n"); } } private void onDataReceivedHEX(byte[] data) { SwingUtilities.invokeLater(() -> { // 检查是否允许数据更新 if (!dataUpdateEnabled) { return; // 如果数据更新被禁用,直接返回,不处理数据 } String hexString = bytesToHex(data).toUpperCase(); String displayText = getCurrentTime() + ": " + hexString; appendToDisplay(displayText + "\n"); }); } private void onDataReceivedascii(String data) { SwingUtilities.invokeLater(() -> { // 检查是否允许数据更新 if (!dataUpdateEnabled) { return; // 如果数据更新被禁用,直接返回,不处理数据 } // 直接使用原始数据,不进行编码转换 String displayText = getCurrentTime() + ": " + data; appendToDisplay(displayText + "\n"); }); } private void toggleAutoScroll() { autoScroll = !autoScroll; dataUpdateEnabled = !dataUpdateEnabled; // 切换数据更新状态 staticDataUpdateEnabled = dataUpdateEnabled; // 同步静态变量 if (autoScroll) { scrollButton.setText("暂停"); scrollButton.setBackground(PRIMARY_COLOR); if (staticDataTextArea != null) { staticDataTextArea.setCaretPosition(staticDataTextArea.getDocument().getLength()); } appendAsciiData("[" + getCurrentTime() + "] 开始显示数据\n"); } else { 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"); } } else { // 如果未轮询,则启动 if (lunxun.startPolling()) { pollingButton.setText("停止查询"); pollingButton.setBackground(DANGER_COLOR); onDataReceivedascii("[" + getCurrentTime() + "] strat\n"); } else { showMessage("错误", "无法启动轮询查询,请检查串口连接", "error"); } } } private void sendData() { String text = sendTextField.getText().trim(); if (text.isEmpty()) { showMessage("提示", "请输入要发送的数据", "info"); return; } try { // HEX格式发送 byte[] data = hexStringToByteArray(text); if (data != null && Sendmsg.sendMessage(text)) { onDataReceivedascii("[" + getCurrentTime() + "] send: " + text.toUpperCase() + "\n"); } else { showMessage("错误", "数据发送失败", "error"); } } catch (Exception e) { showMessage("错误", "数据格式错误: " + e.getMessage(), "error"); } } private void appendToDisplay(String text) { // 限制单行长度,防止过长的数据 if (text.length() > MAX_LINE_LENGTH) { text = text.substring(0, MAX_LINE_LENGTH) + "...\n"; } dataTextArea.append(text); // 检查并限制行数 checkAndTrimLines(); // 根据自动滚动设置决定是否滚动到底部 if (autoScroll) { dataTextArea.setCaretPosition(dataTextArea.getDocument().getLength()); } } // 新增:检查并修剪行数的方法 private void checkAndTrimLines() { try { String currentText = dataTextArea.getText(); String[] lines = currentText.split("\n"); if (lines.length > TRIM_THRESHOLD) { // 保留最近的行 StringBuilder sb = new StringBuilder(); int startIndex = Math.max(0, lines.length - MAX_DISPLAY_LINES); for (int i = startIndex; i < lines.length; i++) { sb.append(lines[i]).append("\n"); } dataTextArea.setText(sb.toString()); } } catch (Exception e) { System.err.println("修剪行数时发生错误: " + e.getMessage()); } } // 新增:清空数据方法 private void clearData() { dataTextArea.setText(""); System.gc(); // 建议垃圾回收 } private String getCurrentTime() { return timeFormat.format(new Date()); } // 工具方法:HEX字符串转字节数组 private byte[] hexStringToByteArray(String s) { s = s.replaceAll("\\s+", ""); // 移除空格 int len = s.length(); if (len % 2 != 0) { throw new IllegalArgumentException("HEX字符串长度必须为偶数"); } byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); } return data; } // 工具方法:字节数组转HEX字符串 private 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(); } // 新增:静态方法的行数检查 private static void checkAndTrimLinesStatic() { if (staticDataTextArea == null) return; try { String currentText = staticDataTextArea.getText(); String[] lines = currentText.split("\n"); if (lines.length > TRIM_THRESHOLD) { // 保留最近的行 StringBuilder sb = new StringBuilder(); int startIndex = Math.max(0, lines.length - MAX_DISPLAY_LINES); for (int i = startIndex; i < lines.length; i++) { sb.append(lines[i]).append("\n"); } staticDataTextArea.setText(sb.toString()); } } catch (Exception e) { System.err.println("修剪行数时发生错误: " + e.getMessage()); } } // 修改为静态方法,供其他类调用 public static void appendHexData(byte[] data) { // 添加状态检查 if (!staticDataUpdateEnabled || staticDataTextArea == null) { return; } SwingUtilities.invokeLater(() -> { try { String hexString = bytesToHex(data).toUpperCase(); String displayText = staticTimeFormat.format(new Date()) + ": " + hexString; // 限制单行长度 if (displayText.length() > MAX_LINE_LENGTH) { displayText = displayText.substring(0, MAX_LINE_LENGTH) + "..."; } staticDataTextArea.append(displayText + "\n"); // 检查并限制行数 checkAndTrimLinesStatic(); // 自动滚动到底部 if (staticDataUpdateEnabled) { staticDataTextArea.setCaretPosition(staticDataTextArea.getDocument().getLength()); } } catch (Exception e) { System.err.println("显示数据时发生错误: " + e.getMessage()); } }); } // 辅助的ASCII数据添加方法(也改为静态) public static void appendAsciiData(String data) { if (!staticDataUpdateEnabled || staticDataTextArea == null) { return; } SwingUtilities.invokeLater(() -> { try { String displayText = staticTimeFormat.format(new Date()) + ": " + data; // 限制单行长度 if (displayText.length() > MAX_LINE_LENGTH) { displayText = displayText.substring(0, MAX_LINE_LENGTH) + "..."; } staticDataTextArea.append(displayText); if (staticDataUpdateEnabled) { staticDataTextArea.setCaretPosition(staticDataTextArea.getDocument().getLength()); } } catch (Exception e) { System.err.println("显示ASCII数据时发生错误: " + e.getMessage()); } }); } // 新增:内存监控 private void startMemoryMonitoring() { memoryMonitorTimer = new Timer((int) MEMORY_CHECK_INTERVAL, e -> { Runtime runtime = Runtime.getRuntime(); long usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024); long maxMemory = runtime.maxMemory() / (1024 * 1024); // 如果内存使用超过80%,建议清理 if (usedMemory > maxMemory * 0.8) { System.out.println("内存使用警告: " + usedMemory + "MB/" + maxMemory + "MB"); // 可选:自动清理旧数据 if (dataTextArea.getLineCount() > MAX_DISPLAY_LINES) { clearData(); appendAsciiData("[" + getCurrentTime() + "] 内存优化:自动清空数据\n"); } } }); memoryMonitorTimer.start(); } private void showMessage(String title, String message, String type) { int messageType; switch (type) { case "error": messageType = JOptionPane.ERROR_MESSAGE; break; case "warning": messageType = JOptionPane.WARNING_MESSAGE; break; default: messageType = JOptionPane.INFORMATION_MESSAGE; } JOptionPane.showMessageDialog(this, message, title, messageType); } private Color brighterColor(Color color) { int r = Math.min(255, color.getRed() + 30); int g = Math.min(255, color.getGreen() + 30); int b = Math.min(255, color.getBlue() + 30); return new Color(r, g, b); } private ImageIcon createIcon(String emoji, int size) { JLabel label = new JLabel(emoji); label.setFont(new Font("Segoe UI Emoji", Font.PLAIN, size)); label.setSize(size, size); // 创建一个图像 java.awt.image.BufferedImage image = new java.awt.image.BufferedImage( size, size, java.awt.image.BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = image.createGraphics(); label.print(g2); g2.dispose(); return new ImageIcon(image); } // 静态方法:从系统设置页面调用 public static void showSystemDebugDialog(JFrame parent) { SwingUtilities.invokeLater(() -> { SystemDebugDialog dialog = new SystemDebugDialog(parent); dialog.setVisible(true); }); } @Override public void dispose() { // 停止内存监控定时器 if (memoryMonitorTimer != null) { memoryMonitorTimer.stop(); } // 关闭时停止轮询 if (lunxun.isPolling()) { lunxun.stopPolling(); } // 停止串口数据捕获 if (serialService != null) { serialService.stopCapture(); } // 清理静态引用,防止内存泄漏 staticDataTextArea = null; // 建议垃圾回收 System.gc(); super.dispose(); } }