package xitongshezhi;
|
|
import javax.swing.*;
|
import javax.swing.border.EmptyBorder;
|
|
import chuankou.Sendmsg;
|
import chuankou.SerialPortService;
|
|
import java.awt.*;
|
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;
|
|
// 屏幕尺寸常量 - 适配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 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 =false; // 默认自动滚动
|
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("宋体", Font.PLAIN, 12));
|
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());
|
|
|
// 清空按钮
|
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(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");
|
lunxun.resumePolling();
|
lunxun.DEBUG_ENABLED=true;
|
} else {
|
scrollButton.setText("暂停");
|
scrollButton.setBackground(WARNING_COLOR);
|
appendAsciiData("[" + getCurrentTime() + "] 暂停显示数据\n");
|
}
|
}
|
|
|
|
|
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) {
|
Errlog.logOperation("修剪行数时发生错误: " + 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) {
|
Errlog.logOperation("修剪行数时发生错误: " + 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) {
|
Errlog.logOperation("显示数据时发生错误: " + 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+"\n");
|
|
if (staticDataUpdateEnabled) {
|
staticDataTextArea.setCaretPosition(staticDataTextArea.getDocument().getLength());
|
}
|
|
} catch (Exception e) {
|
Errlog.logOperation("显示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();
|
}
|
|
// 重要修改:不要停止轮询,只暂停调试输出
|
lunxun.setDEBUG_ENABLED(false);
|
|
// 重要修改:不要停止串口数据捕获,只移除自己的回调
|
if (serialService != null) {
|
// 使用新的方法移除回调而不停止整个捕获
|
removeDataReceivedCallback();
|
}
|
|
// 清理静态引用,防止内存泄漏
|
staticDataTextArea = null;
|
|
// 建议垃圾回收
|
System.gc();
|
|
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) {
|
}
|
}
|