a8a90fd2d5040e66cc66b3da122a7f82561ff6f6..fb883547ede83b1c758b1a9a025898ba3f83497a
9 天以前 826220679@qq.com
新增
fb8835 对比 | 目录
9 天以前 826220679@qq.com
修改
176cfa 对比 | 目录
已修改10个文件
已重命名1个文件
1007 ■■■■■ 文件已修改
src/dell55AAData/Dell55AA01Parser.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dell_system/MessageViewPanel.java 600 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dell_system/SerialCommPanel.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dell_system/SerialPacketParser.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/udptcp/PacketParser.java 79 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/udptcp/PacketProcessingSystem.java 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/udptcp/UDPPortAReceiver.java 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/window/WelcomeFrame.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/Messages_en.properties 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/Messages_zh.properties 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/logfile/openlog.txt 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dell55AAData/Dell55AA01Parser.java
@@ -1,7 +1,9 @@
package dell55AAData;
import dell_system.MessageViewPanel;
public class Dell55AA01Parser {
    // åè®®å¸¸é‡
    // å¸¸é‡å®šä¹‰
    private static final String EXPECTED_HEADER = "55AA01"; // åè®®å¤´
    private static final int MIN_LENGTH = 42; // æœ€å°é•¿åº¦(21字节*2字符)
    private static final ThreadLocal<ParseResult> RESULT_CACHE = 
@@ -10,8 +12,8 @@
    // è§£æžç»“果类
    public static class ParseResult {
        public int sequenceNum;   // åºåˆ—号
        public String tagId;      // æ ‡ç­¾ID(4字节十六进制)
        public String anchorId;   // é”šç‚¹ID(4字节十六进制)
        public String tagId;      // æ ‡ç­¾ID(4字节小端序)
        public String anchorId;   // é”šç‚¹ID(4字节小端序)
        public int distance;      // è·ç¦»(毫米)
        public int power;         // ç”µé‡(0-100)
        public boolean buttonPressed; // æŒ‰é’®çŠ¶æ€
@@ -29,55 +31,73 @@
    /**
     * è§£æž55AA01协议数据
     * @param message åå…­è¿›åˆ¶å­—符串
     * @return è§£æžç»“æžœ(失败返回null)
     * @return è§£æžç»“æžœ(错误时返回null)
     */
    public static ParseResult parse(String message) {
        // å‚数检查
    public static ParseResult parse(String message, String ip, int port) {
        if (message == null || message.isEmpty()) {
            return null;
        }
        // æ¸…洗数据:移除所有非十六进制字符
        message = message.replaceAll("[^0-9A-Fa-f]", "").toUpperCase();
        // æ•°æ®æ ¡éªŒ
        if (message == null || message.length() < MIN_LENGTH) {
            return null;
        }
        
        // åè®®å¤´éªŒè¯ (55AA01)
        // åè®®å¤´æ ¡éªŒ (55AA01)
        for (int i = 0; i < 6; i++) {
            if (message.charAt(i) != EXPECTED_HEADER.charAt(i)) {
                return null;
            }
        }
        
        // èŽ·å–çº¿ç¨‹æœ¬åœ°èµ„æº
        ParseResult result = RESULT_CACHE.get();
        result.reset();
        char[] chars = HexUtils.getThreadLocalBuffer();
        message.getChars(0, Math.min(message.length(), chars.length), chars, 0);
        
        // è§£æžåºåˆ—号 (位置8-9)
        result.sequenceNum = HexUtils.fastHexToByte(chars[8], chars[9]);
        // è§£æžæ ‡ç­¾ID (位置10-13, å°ç«¯åº)
        result.tagId = new String(new char[] {
            chars[12], chars[13], // é«˜ä½
            chars[10], chars[11]   // ä½Žä½
        });
        // è§£æžé”šç‚¹ID (位置14-17, å°ç«¯åº)
        result.anchorId = new String(new char[] {
            chars[16], chars[17], // é«˜ä½
            chars[14], chars[15]   // ä½Žä½
        });
        // è§£æžè·ç¦» (位置18-25, 4字节小端序整数)
        int b0 = HexUtils.fastHexToByte(chars[18], chars[19]); // æœ€ä½Žä½
        int b1 = HexUtils.fastHexToByte(chars[20], chars[21]);
        int b2 = HexUtils.fastHexToByte(chars[22], chars[23]);
        int b3 = HexUtils.fastHexToByte(chars[24], chars[25]); // æœ€é«˜ä½
        int raw = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
        result.distance = raw; // ä¿æŒåŽŸå§‹æ•´æ•°å€¼
        // è§£æžç”µé‡ (位置26-27)
        result.power = HexUtils.fastHexToByte(chars[26], chars[27]);
        // è§£æžæŒ‰é’®çŠ¶æ€ (位置28-29)
        result.buttonPressed = HexUtils.fastHexToByte(chars[28], chars[29]) == 1;
        try {
            if (message.length() < 30) { // ç¡®ä¿æœ‰è¶³å¤Ÿé•¿åº¦è®¿é—®charAt(28)
                return null;
            }
            // è§£æžåºåˆ—号 (位置8-9)
            result.sequenceNum = HexUtils.fastHexToByte(
                message.charAt(8), message.charAt(9)
            );
            // è§£æžæ ‡ç­¾ID (位置10-13, å°ç«¯åº)
            result.tagId = new String(new char[] {
                message.charAt(12), message.charAt(13), // é«˜ä½
                message.charAt(10), message.charAt(11)   // ä½Žä½
            });
            // è§£æžé”šç‚¹ID (位置14-17, å°ç«¯åº)
            result.anchorId = new String(new char[] {
                message.charAt(16), message.charAt(17), // é«˜ä½
                message.charAt(14), message.charAt(15)   // ä½Žä½
            });
            // è§£æžè·ç¦» (位置18-25, 4字节小端整数)
            int b0 = HexUtils.fastHexToByte(message.charAt(18), message.charAt(19)); // æœ€ä½Žä½
            int b1 = HexUtils.fastHexToByte(message.charAt(20), message.charAt(21));
            int b2 = HexUtils.fastHexToByte(message.charAt(22), message.charAt(23));
            int b3 = HexUtils.fastHexToByte(message.charAt(24), message.charAt(25)); // æœ€é«˜ä½
            int raw = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
            result.distance = raw; // ä¿æŒåŽŸå§‹æ•´æ•°å€¼
            // è§£æžç”µé‡ (位置26-27)
            result.power = HexUtils.fastHexToByte(message.charAt(26), message.charAt(27));
            // è§£æžæŒ‰é’®çŠ¶æ€ (位置28-29)
            result.buttonPressed = HexUtils.fastHexToByte(message.charAt(28), message.charAt(29)) == 1;
            String hexData = "55AA01标签地址:" + result.tagId + "基站地址:" + result.anchorId +
                             "距离:" + result.distance + "电量:" + result.power +
                             "按钮状态:" + result.buttonPressed;
            MessageViewPanel.showData(hexData, ip, port, 0, "UDPA", "55AA01", "ALL");
        } catch (IndexOutOfBoundsException | NumberFormatException e) {
            System.err.println("Parsing error in packet from " + ip + ":" + port);
            return null;
        }
        
        return result;
    }
src/dell_system/MessageViewPanel.java
@@ -16,50 +16,46 @@
import udptcp.UDPPortBReceiver;
public class MessageViewPanel extends JPanel {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    private ResourceBundle messages;   // å›½é™…化消息资源包
    // UI组件声明
    private JButton btnStart, btnPause, btnClear, btnSend; // æŽ§åˆ¶æŒ‰é’®ï¼šå¼€å§‹ã€æš‚停、清除、发送
    private static JComboBox<String> cbDataType; // ä¸‹æ‹‰æ¡†ï¼šæ•°æ®ç±»åž‹ã€åè®®ã€è®¾å¤‡ID
    private ResourceBundle messages;
    private JButton btnStart, btnPause, btnClear, btnSend;
    private static JComboBox<String> cbDataType;
    private static JComboBox<String> cbProtocol;
    private static JComboBox<String> cbDeviceId;
    private static JComboBox<String> cbRemoteDevice; // è¿œç¨‹è®¾å¤‡ä¸‹æ‹‰æ¡†
    private static JCheckBox chkAutoSave; // å¤é€‰æ¡†ï¼šè‡ªåŠ¨ä¿å­˜ã€æ˜¾ç¤ºæ¥æºã€åå…­è¿›åˆ¶æ˜¾ç¤ºã€ASCII显示
    private static JComboBox<String> cbRemoteDevice;
    private static JCheckBox chkAutoSave;
    private static JCheckBox chkShowSource;
    private static JCheckBox chkHexDisplay;
    private static JCheckBox chkAsciiDisplay;
    private static JTextArea txtDataView; // æ•°æ®æ˜¾ç¤ºåŒºåŸŸ
    private JScrollPane scrollPane; // å¸¦æ»šåŠ¨æ¡çš„é¢æ¿
    private JTextField txtSendData; // å‘送数据文本框
    private JRadioButton rdoSendHex; // HEX发送单选按钮
    private JRadioButton rdoSendAscii; // ASCII发送单选按钮
    private JCheckBox chkAppendNewline; // å›žè½¦æ¢è¡Œå¤é€‰æ¡†
    // æ·»åŠ é™æ€å˜é‡è·Ÿè¸ªçª—å£å¯è§çŠ¶æ€
    private static JTextArea txtDataView;
    private JScrollPane scrollPane;
    private JTextField txtSendData;
    private JRadioButton rdoSendHex;
    private JRadioButton rdoSendAscii;
    private JCheckBox chkAppendNewline;
    private static final ThreadLocal<StringBuilder> SB =
            ThreadLocal.withInitial(() -> new StringBuilder(256));
    private static final char[] DIGITS = "0123456789".toCharArray();
    private static final String COLON = ":";
    private static final String PREFIX = "[";
    private static final String SUFFIX = "]";
    public static volatile boolean isWindowVisible = false;
    // çŠ¶æ€å˜é‡
    private static boolean isCapturing = false; // æ˜¯å¦æ­£åœ¨æ•获数据的标志
    // ä¿®æ”¹ä¸ºMap存储设备及其本地端口
    private static boolean isCapturing = false;
    private static boolean isStarted = false; // æ–°å¢žï¼šè·Ÿè¸ªå¼€å§‹æŒ‰é’®çŠ¶æ€
    private static Map<String, Integer> remoteDeviceLocalPortMap = new HashMap<>();
    // æž„造函数
    public MessageViewPanel(ResourceBundle messages) {
        this.messages = messages; // åˆå§‹åŒ–消息资源包
        initializeUI(); // åˆå§‹åŒ–用户界面
    }
    public MessageViewPanel(ResourceBundle messages) {
        this.messages = messages;
        initializeUI();
    }
    public void addNotify() {
        super.addNotify();
        Window window = SwingUtilities.getWindowAncestor(this);
        if (window != null) {
            isWindowVisible = true; // é¢æ¿å·²æ·»åŠ åˆ°å¯è§çª—å£
            isWindowVisible = true;
            window.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
@@ -74,154 +70,129 @@
        }
    }
    // åˆå§‹åŒ–UI组件
    private void initializeUI() {
        setLayout(new GridBagLayout()); // ä½¿ç”¨ç½‘格袋布局
        GridBagConstraints gbc = new GridBagConstraints(); // å¸ƒå±€çº¦æŸå¯¹è±¡
        gbc.insets = new Insets(5, 10, 5, 10); // è®¾ç½®ç»„件间距(上、左、下、右)
        gbc.anchor = GridBagConstraints.WEST; // ç»„件靠左对齐
        gbc.fill = GridBagConstraints.HORIZONTAL; // æ°´å¹³å¡«å……
        setBackground(new Color(240, 245, 249)); // è®¾ç½®èƒŒæ™¯è‰²
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5, 10, 5, 10);
        gbc.anchor = GridBagConstraints.WEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        setBackground(new Color(240, 245, 249));
        // ä¸‹æ‹‰æ¡†åŒºåŸŸ
        gbc.gridx = 0; // èµ·å§‹åˆ—
        gbc.gridy = 0; // èµ·å§‹è¡Œ
        gbc.gridwidth = 1; // å ç”¨1列宽度
        gbc.weightx = 0.25; // æ°´å¹³æƒé‡
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = 1;
        gbc.weightx = 0.25;
        // æ•°æ®ç±»åž‹ä¸‹æ‹‰æ¡†é¢æ¿
        JPanel dataTypePanel = new JPanel(new BorderLayout(5, 0)); // ä½¿ç”¨è¾¹ç•Œå¸ƒå±€
        dataTypePanel.setBackground(new Color(240, 245, 249)); // è®¾ç½®èƒŒæ™¯è‰²
        dataTypePanel.add(createLabel(messages.getString("MESSAGE_VIEW_DATA_TYPE")), BorderLayout.WEST); // æ·»åŠ æ ‡ç­¾
        cbDataType = new JComboBox<>(); // åˆ›å»ºä¸‹æ‹‰æ¡†
        loadtyps();// æ·»åŠ "所有类型"选项
        dataTypePanel.add(cbDataType, BorderLayout.CENTER); // å°†ä¸‹æ‹‰æ¡†æ·»åŠ åˆ°é¢æ¿ä¸­å¤®
        add(dataTypePanel, gbc); // å°†é¢æ¿æ·»åŠ åˆ°ä¸»é¢æ¿
        JPanel dataTypePanel = new JPanel(new BorderLayout(5, 0));
        dataTypePanel.setBackground(new Color(240, 245, 249));
        dataTypePanel.add(createLabel(messages.getString("MESSAGE_VIEW_DATA_TYPE")), BorderLayout.WEST);
        cbDataType = new JComboBox<>();
        loadtyps();
        dataTypePanel.add(cbDataType, BorderLayout.CENTER);
        add(dataTypePanel, gbc);
        // åè®®ä¸‹æ‹‰æ¡†
        gbc.gridx++; // ç§»åŠ¨åˆ°ä¸‹ä¸€åˆ—
        JPanel protocolPanel = new JPanel(new BorderLayout(5, 0)); // åè®®é€‰æ‹©é¢æ¿
        protocolPanel.setBackground(new Color(240, 245, 249)); // è®¾ç½®èƒŒæ™¯è‰²
        protocolPanel.add(createLabel(messages.getString("MESSAGE_VIEW_PROTOCOL")), BorderLayout.WEST); // æ·»åŠ åè®®æ ‡ç­¾
        cbProtocol = new JComboBox<>(new String[]{ // åˆ›å»ºåè®®ä¸‹æ‹‰æ¡†å¹¶åˆå§‹åŒ–选项
                messages.getString("MESSAGE_VIEW_ALL_PROTOCOLS"), // æ‰€æœ‰åè®®
                "UDP", // UDP协议
                "TCP", // TCP协议
                "MQTT", // MQTT协议
                "HTTP" // HTTP协议
        gbc.gridx++;
        JPanel protocolPanel = new JPanel(new BorderLayout(5, 0));
        protocolPanel.setBackground(new Color(240, 245, 249));
        protocolPanel.add(createLabel(messages.getString("MESSAGE_VIEW_PROTOCOL")), BorderLayout.WEST);
        cbProtocol = new JComboBox<>(new String[]{
                "ALL",
                "55AA01", "55AA02", "55AA03", "55AA05", "55AA07",
                "55AA0A", "55AA0C", "55AA12", "55AA14", "55AA20",
                "GNGGA", "XTB"
        });
        protocolPanel.add(cbProtocol, BorderLayout.CENTER); // å°†ä¸‹æ‹‰æ¡†æ·»åŠ åˆ°é¢æ¿ä¸­å¤®
        add(protocolPanel, gbc); // å°†é¢æ¿æ·»åŠ åˆ°ä¸»é¢æ¿
        protocolPanel.add(cbProtocol, BorderLayout.CENTER);
        add(protocolPanel, gbc);
        // è®¾å¤‡ID下拉框
        gbc.gridx++; // ç§»åŠ¨åˆ°ä¸‹ä¸€åˆ—
        JPanel devicePanel = new JPanel(new BorderLayout(5, 0)); // è®¾å¤‡ID选择面板
        devicePanel.setBackground(new Color(240, 245, 249)); // è®¾ç½®èƒŒæ™¯è‰²
        devicePanel.add(createLabel(messages.getString("MESSAGE_VIEW_DEVICE_ID")), BorderLayout.WEST); // æ·»åŠ è®¾å¤‡ID标签
        cbDeviceId = new JComboBox<>(); // åˆ›å»ºè®¾å¤‡ID下拉框
        cbDeviceId.setEditable(true); // è®¾ç½®ä¸ºå¯ç¼–辑,允许手动输入设备ID
        cbDeviceId.addItem(messages.getString("MESSAGE_VIEW_ALL_DEVICES")); // æ·»åŠ "所有设备"选项
        loadDeviceIds(); // åŠ è½½è®¾å¤‡ID列表
        devicePanel.add(cbDeviceId, BorderLayout.CENTER); // å°†ä¸‹æ‹‰æ¡†æ·»åŠ åˆ°é¢æ¿ä¸­å¤®
        add(devicePanel, gbc); // å°†é¢æ¿æ·»åŠ åˆ°ä¸»é¢æ¿
        gbc.gridx++;
        JPanel devicePanel = new JPanel(new BorderLayout(5, 0));
        devicePanel.setBackground(new Color(240, 245, 249));
        devicePanel.add(createLabel(messages.getString("MESSAGE_VIEW_DEVICE_ID")), BorderLayout.WEST);
        cbDeviceId = new JComboBox<>();
        cbDeviceId.setEditable(true);
        cbDeviceId.addItem("ALL");
        loadDeviceIds();
        devicePanel.add(cbDeviceId, BorderLayout.CENTER);
        add(devicePanel, gbc);
        // è¿œç¨‹è®¾å¤‡ä¸‹æ‹‰æ¡†
        gbc.gridx++; // ç§»åŠ¨åˆ°ä¸‹ä¸€åˆ—
        JPanel remoteDevicePanel = new JPanel(new BorderLayout(5, 0)); // è¿œç¨‹è®¾å¤‡é€‰æ‹©é¢æ¿
        remoteDevicePanel.setBackground(new Color(240, 245, 249)); // è®¾ç½®èƒŒæ™¯è‰²
        remoteDevicePanel.add(createLabel(messages.getString("MESSAGE_VIEW_REMOTE_DEVICE")), BorderLayout.WEST); // æ·»åŠ è¿œç¨‹è®¾å¤‡æ ‡ç­¾
        cbRemoteDevice = new JComboBox<>(); // åˆ›å»ºè¿œç¨‹è®¾å¤‡ä¸‹æ‹‰æ¡†
        cbRemoteDevice.setEditable(true); // è®¾ç½®ä¸ºå¯ç¼–辑
        cbRemoteDevice.addItem(messages.getString("MESSAGE_VIEW_ALL_DEVICES")); // æ·»åŠ "所有设备"选项
        remoteDevicePanel.add(cbRemoteDevice, BorderLayout.CENTER); // å°†ä¸‹æ‹‰æ¡†æ·»åŠ åˆ°é¢æ¿ä¸­å¤®
        add(remoteDevicePanel, gbc); // å°†é¢æ¿æ·»åŠ åˆ°ä¸»é¢æ¿
        gbc.gridx++;
        JPanel remoteDevicePanel = new JPanel(new BorderLayout(5, 0));
        remoteDevicePanel.setBackground(new Color(240, 245, 249));
        remoteDevicePanel.add(createLabel(messages.getString("MESSAGE_VIEW_REMOTE_DEVICE")), BorderLayout.WEST);
        cbRemoteDevice = new JComboBox<>();
        cbRemoteDevice.setEditable(true);
        cbRemoteDevice.addItem(messages.getString("MESSAGE_VIEW_ALL_DEVICES"));
        remoteDevicePanel.add(cbRemoteDevice, BorderLayout.CENTER);
        add(remoteDevicePanel, gbc);
        // å¤é€‰æ¡†åŒºåŸŸ
        gbc.gridx = 0; // é‡ç½®åˆ—位置
        gbc.gridy++; // ç§»åŠ¨åˆ°ä¸‹ä¸€è¡Œ
        gbc.gridwidth = 4; // æ¨ªè·¨4列
        gbc.weightx = 1.0; // æ°´å¹³æƒé‡
        // å¤é€‰æ¡†åŒºåŸŸ - ç®€åŒ–为两个复选框
        gbc.gridx = 0;
        gbc.gridy++;
        gbc.gridwidth = 4;
        gbc.weightx = 1.0;
        JPanel checkboxPanel = new JPanel(new GridLayout(1, 4, 6, 0)); // åˆ›å»ºç½‘格布局面板(1行4列)
        checkboxPanel.setBackground(new Color(240, 245, 249)); // è®¾ç½®èƒŒæ™¯è‰²
        chkAutoSave = createStyledCheckbox(messages.getString("MESSAGE_VIEW_AUTO_SAVE")); // åˆ›å»ºè‡ªåŠ¨ä¿å­˜å¤é€‰æ¡†
        chkShowSource = createStyledCheckbox(messages.getString("MESSAGE_VIEW_SHOW_SOURCE")); // åˆ›å»ºæ˜¾ç¤ºæ¥æºå¤é€‰æ¡†
        chkHexDisplay = createStyledCheckbox(messages.getString("MESSAGE_VIEW_HEX_DISPLAY")); // åˆ›å»ºåå…­è¿›åˆ¶æ˜¾ç¤ºå¤é€‰æ¡†
        chkAsciiDisplay = createStyledCheckbox(messages.getString("MESSAGE_VIEW_ASCII_DISPLAY")); // åˆ›å»ºASCII显示复选框
        // å°†å¤é€‰æ¡†æ·»åŠ åˆ°é¢æ¿
        JPanel checkboxPanel = new JPanel(new GridLayout(1, 2, 6, 0)); // ä¿®æ”¹ä¸º2列
        checkboxPanel.setBackground(new Color(240, 245, 249));
        chkAutoSave = createStyledCheckbox(messages.getString("MESSAGE_VIEW_AUTO_SAVE"));
        chkShowSource = createStyledCheckbox(messages.getString("MESSAGE_VIEW_SHOW_SOURCE"));
        // ç§»é™¤äº†åå…­è¿›åˆ¶å’ŒASCII显示复选框
        checkboxPanel.add(chkAutoSave);
        checkboxPanel.add(chkShowSource);
        checkboxPanel.add(chkHexDisplay);
        checkboxPanel.add(chkAsciiDisplay);
        add(checkboxPanel, gbc); // å°†å¤é€‰æ¡†é¢æ¿æ·»åŠ åˆ°ä¸»é¢æ¿
        add(checkboxPanel, gbc);
        // æ•°æ®æ˜¾ç¤ºåŒºåŸŸ
        gbc.gridy++; // ç§»åŠ¨åˆ°ä¸‹ä¸€è¡Œ
        gbc.weighty = 1.0; // åž‚直权重
        gbc.fill = GridBagConstraints.BOTH; // åŒå‘å¡«å……
        gbc.gridy++;
        gbc.weighty = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        txtDataView = new JTextArea(); // åˆ›å»ºæ–‡æœ¬åŒºåŸŸ
        txtDataView.setFont(new Font("Monospaced", Font.PLAIN, 14)); // è®¾ç½®ç­‰å®½å­—体
        txtDataView.setEditable(false); // è®¾ç½®ä¸ºä¸å¯ç¼–辑
        scrollPane = new JScrollPane(txtDataView); // åˆ›å»ºå¸¦æ»šåŠ¨æ¡çš„é¢æ¿
        txtDataView = new JTextArea();
        txtDataView.setFont(new Font("Monospaced", Font.PLAIN, 14));
        txtDataView.setEditable(false);
        scrollPane = new JScrollPane(txtDataView);
        scrollPane.setPreferredSize(new Dimension(800, 400));
        scrollPane.setBorder(BorderFactory.createTitledBorder( // è®¾ç½®å¸¦æ ‡é¢˜çš„边框
                BorderFactory.createLineBorder(new Color(180, 180, 220)), // è¾¹æ¡†é¢œè‰²
                messages.getString("MESSAGE_VIEW_DATA_PANEL"), // æ ‡é¢˜æ–‡æœ¬
                TitledBorder.LEFT, // æ ‡é¢˜ä½ç½®
                TitledBorder.TOP, // æ ‡é¢˜ä½ç½®
                new Font("微软雅黑", Font.BOLD, 12), // æ ‡é¢˜å­—体
                new Color(70, 130, 180) // æ ‡é¢˜é¢œè‰²
                ));
        scrollPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createLineBorder(new Color(180, 180, 220)),
                messages.getString("MESSAGE_VIEW_DATA_PANEL"),
                TitledBorder.LEFT,
                TitledBorder.TOP,
                new Font("微软雅黑", Font.BOLD, 12),
                new Color(70, 130, 180)
        ));
        add(scrollPane, gbc); // å°†æ»šåŠ¨é¢æ¿æ·»åŠ åˆ°ä¸»é¢æ¿
        add(scrollPane, gbc);
        // æŒ‰é’®é¢æ¿åŒºåŸŸ
        gbc.gridy++; // ç§»åŠ¨åˆ°ä¸‹ä¸€è¡Œ
        gbc.weighty = 0; // é‡ç½®åž‚直权重
        gbc.fill = GridBagConstraints.HORIZONTAL; // æ°´å¹³å¡«å……
        gbc.gridy++;
        gbc.weighty = 0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        // æŒ‰é’®é¢æ¿
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5)); // ä½¿ç”¨æµå¼å¸ƒå±€ï¼ˆå·¦å¯¹é½ï¼‰
        buttonPanel.setBackground(new Color(240, 245, 249)); // è®¾ç½®èƒŒæ™¯è‰²
        btnStart = createStyledButton(messages.getString("MESSAGE_VIEW_START"), new Color(70, 130, 180)); // åˆ›å»ºå¼€å§‹æŒ‰é’®ï¼ˆè“è‰²ï¼‰
        btnPause = createStyledButton(messages.getString("MESSAGE_VIEW_PAUSE"), new Color(218, 165, 32)); // åˆ›å»ºæš‚停按钮(金色)
        btnClear = createStyledButton(messages.getString("MESSAGE_VIEW_CLEAR"), new Color(205, 92, 92)); // åˆ›å»ºæ¸…除按钮(红色)
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5));
        buttonPanel.setBackground(new Color(240, 245, 249));
        btnStart = createStyledButton(messages.getString("MESSAGE_VIEW_START"), new Color(70, 130, 180));
        btnPause = createStyledButton(messages.getString("MESSAGE_VIEW_PAUSE"), new Color(218, 165, 32));
        btnClear = createStyledButton(messages.getString("MESSAGE_VIEW_CLEAR"), new Color(205, 92, 92));
        // å‘送相关控件
        txtSendData = new JTextField(30); // å‘送数据文本框
        txtSendData.setFont(new Font("微软雅黑", Font.PLAIN, 12)); // è®¾ç½®å­—体
        btnSend = createStyledButton(messages.getString("MESSAGE_VIEW_SEND"), new Color(60, 179, 113)); // åˆ›å»ºå‘送按钮(绿色)
        txtSendData = new JTextField(30);
        txtSendData.setFont(new Font("微软雅黑", Font.PLAIN, 12));
        btnSend = createStyledButton(messages.getString("MESSAGE_VIEW_SEND"), new Color(60, 179, 113));
        // åˆ›å»ºå‘送选项面板
        JPanel sendOptionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
        sendOptionsPanel.setBackground(new Color(240, 245, 249));
        // åˆ›å»ºå•选按钮组
        ButtonGroup sendFormatGroup = new ButtonGroup();
        // åˆ›å»ºHEX发送单选按钮
        rdoSendHex = createStyledRadioButton(messages.getString("MESSAGE_VIEW_SEND_HEX"));
        // åˆ›å»ºASCII发送单选按钮
        rdoSendAscii = createStyledRadioButton(messages.getString("MESSAGE_VIEW_SEND_ASCII"));
        // å°†å•选按钮添加到按钮组
        sendFormatGroup.add(rdoSendHex);
        sendFormatGroup.add(rdoSendAscii);
        // é»˜è®¤é€‰ä¸­ASCII发送
        rdoSendAscii.setSelected(true);
        // å›žè½¦æ¢è¡Œå¤é€‰æ¡†
        chkAppendNewline = createStyledCheckbox(messages.getString("MESSAGE_VIEW_APPEND_NEWLINE"));
        sendOptionsPanel.add(rdoSendHex);
        sendOptionsPanel.add(rdoSendAscii);
        sendOptionsPanel.add(chkAppendNewline);
        // å°†æŒ‰é’®æ·»åŠ åˆ°æŒ‰é’®é¢æ¿
        buttonPanel.add(btnStart);
        buttonPanel.add(btnPause);
        buttonPanel.add(btnClear);
@@ -229,55 +200,46 @@
        buttonPanel.add(btnSend);
        buttonPanel.add(sendOptionsPanel);
        add(buttonPanel, gbc); // å°†æŒ‰é’®é¢æ¿æ·»åŠ åˆ°ä¸»é¢æ¿
        add(buttonPanel, gbc);
        // è®¾ç½®æŒ‰é’®åŠ¨ä½œ
        setupActions();
    }
    // åˆ›å»ºå¸¦æ ·å¼çš„æ ‡ç­¾
    private JLabel createLabel(String text) {
        JLabel label = new JLabel(text); // åˆ›å»ºæ ‡ç­¾
        label.setFont(new Font("微软雅黑", Font.BOLD, 12)); // è®¾ç½®å­—体
        label.setForeground(new Color(70, 70, 70)); // è®¾ç½®å‰æ™¯è‰²
        JLabel label = new JLabel(text);
        label.setFont(new Font("微软雅黑", Font.BOLD, 12));
        label.setForeground(new Color(70, 70, 70));
        return label;
    }
    // åˆ›å»ºå¸¦æ ·å¼çš„æŒ‰é’®
    private JButton createStyledButton(String text, Color bgColor) {
        JButton button = new JButton(text); // åˆ›å»ºæŒ‰é’®
        button.setFont(new Font("微软雅黑", Font.BOLD, 12)); // è®¾ç½®å­—体
        button.setBackground(bgColor); // è®¾ç½®èƒŒæ™¯è‰²
        button.setForeground(Color.WHITE); // è®¾ç½®å‰æ™¯è‰²ï¼ˆç™½è‰²ï¼‰
        button.setFocusPainted(false); // ç¦ç”¨ç„¦ç‚¹è¾¹æ¡†
        button.setBorder(BorderFactory.createCompoundBorder( // è®¾ç½®å¤åˆè¾¹æ¡†
                BorderFactory.createLineBorder(bgColor.darker(), 1), // å¤–部边框(深色)
                BorderFactory.createEmptyBorder(5, 15, 5, 15) // å†…部边距
                ));
        JButton button = new JButton(text);
        button.setFont(new Font("微软雅黑", Font.BOLD, 12));
        button.setBackground(bgColor);
        button.setForeground(Color.WHITE);
        button.setFocusPainted(false);
        button.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(bgColor.darker(), 1),
                BorderFactory.createEmptyBorder(5, 15, 5, 15)
        ));
        return button;
    }
    // åˆ›å»ºå¸¦æ ·å¼çš„复选框
    private JCheckBox createStyledCheckbox(String text) {
        JCheckBox checkBox = new JCheckBox(text); // åˆ›å»ºå¤é€‰æ¡†
        checkBox.setFont(new Font("微软雅黑", Font.PLAIN, 12)); // è®¾ç½®å­—体
        checkBox.setBackground(new Color(240, 245, 249)); // è®¾ç½®èƒŒæ™¯è‰²
        checkBox.setForeground(new Color(70, 70, 70)); // è®¾ç½®å‰æ™¯è‰²
        JCheckBox checkBox = new JCheckBox(text);
        checkBox.setFont(new Font("微软雅黑", Font.PLAIN, 12));
        checkBox.setBackground(new Color(240, 245, 249));
        checkBox.setForeground(new Color(70, 70, 70));
        return checkBox;
    }
    // åˆ›å»ºå¸¦æ ·å¼çš„单选按钮
    private JRadioButton createStyledRadioButton(String text) {
        JRadioButton radioButton = new JRadioButton(text); // åˆ›å»ºå•选按钮
        radioButton.setFont(new Font("微软雅黑", Font.PLAIN, 12)); // è®¾ç½®å­—体
        radioButton.setBackground(new Color(240, 245, 249)); // è®¾ç½®èƒŒæ™¯è‰²
        radioButton.setForeground(new Color(70, 70, 70)); // è®¾ç½®å‰æ™¯è‰²
        // ç¾ŽåŒ–选中效果
        JRadioButton radioButton = new JRadioButton(text);
        radioButton.setFont(new Font("微软雅黑", Font.PLAIN, 12));
        radioButton.setBackground(new Color(240, 245, 249));
        radioButton.setForeground(new Color(70, 70, 70));
        radioButton.setIcon(new RadioButtonIcon(false));
        radioButton.setSelectedIcon(new RadioButtonIcon(true));
        // æ·»åŠ é¼ æ ‡æ‚¬åœæ•ˆæžœ
        radioButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseEntered(java.awt.event.MouseEvent evt) {
                radioButton.setForeground(new Color(30, 144, 255));
@@ -286,11 +248,9 @@
                radioButton.setForeground(new Color(70, 70, 70));
            }
        });
        return radioButton;
    }
    // è‡ªå®šä¹‰å•选按钮图标
    private static class RadioButtonIcon implements Icon {
        private static final int SIZE = 14;
        private final boolean selected;
@@ -305,19 +265,14 @@
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            if (selected) {
                // é€‰ä¸­çŠ¶æ€ - è“è‰²å¡«å……
                g2d.setColor(new Color(70, 130, 180));
                g2d.fillOval(x, y, SIZE, SIZE);
                // å†…圆 - ç™½è‰²
                g2d.setColor(Color.WHITE);
                g2d.fillOval(x + SIZE/4, y + SIZE/4, SIZE/2, SIZE/2);
            } else {
                // æœªé€‰ä¸­çŠ¶æ€ - ç°è‰²è¾¹æ¡†
                g2d.setColor(new Color(150, 150, 150));
                g2d.drawOval(x, y, SIZE-1, SIZE-1);
            }
            g2d.dispose();
        }
@@ -332,77 +287,65 @@
        }
    }
    // åŠ è½½æ•°æ®ç±»åž‹
    private void loadtyps() {
        cbDataType.removeAllItems();
        // æ·»åŠ å›½é™…åŒ–é€‰é¡¹
        cbDataType.addItem(messages.getString("MESSAGE_VIEW_ALL_TYPES"));
        cbDataType.addItem(messages.getString("MESSAGE_VIEW_UDP_PORT_A"));
        cbDataType.addItem(messages.getString("MESSAGE_VIEW_UDP_PORT_B"));
        cbDataType.addItem(messages.getString("MESSAGE_VIEW_TCP_PORT"));
        cbDataType.addItem(messages.getString("MESSAGE_VIEW_MQTT_PORT"));
        cbDataType.addItem("UDPA");
        cbDataType.addItem("UDPB");
        cbDataType.addItem("TCP");
    }
    // è®¾ç½®æŒ‰é’®åŠ¨ä½œ
    private void setupActions() {
        btnStart.addActionListener(e -> startCapture()); // å¼€å§‹æŒ‰é’®ç»‘定开始捕获方法
        btnPause.addActionListener(e -> pauseCapture()); // æš‚停按钮绑定暂停捕获方法
        btnClear.addActionListener(e -> clearDisplay()); // æ¸…除按钮绑定清除显示方法
        btnSend.addActionListener(e -> sendData()); // å‘送按钮绑定发送方法
        btnStart.addActionListener(e -> startCapture());
        btnPause.addActionListener(e -> pauseCapture());
        btnClear.addActionListener(e -> clearDisplay());
        btnSend.addActionListener(e -> sendData());
    }
    // å¼€å§‹æ•获数据
    private void startCapture() {
        if (!isCapturing) { // å¦‚果当前未在捕获状态
            isCapturing = true; // è®¾ç½®æ•获状态为true
            btnStart.setEnabled(false); // ç¦ç”¨å¼€å§‹æŒ‰é’®
            btnPause.setEnabled(true); // å¯ç”¨æš‚停按钮
            // å¯åŠ¨æ•°æ®æ•èŽ·çº¿ç¨‹
        if (!isCapturing) {
            isStarted = true; // è®¾ç½®å¼€å§‹æ ‡å¿—
            isCapturing = true;
            btnStart.setEnabled(false);
            btnPause.setEnabled(true);
        }
    }
    // æš‚停捕获数据
    private void pauseCapture() {
        isCapturing = false; // è®¾ç½®æ•获状态为false
        btnStart.setEnabled(true); // å¯ç”¨å¼€å§‹æŒ‰é’®
        btnPause.setEnabled(false); // ç¦ç”¨æš‚停按钮
        isStarted = false; // æ¸…除开始标志
        isCapturing = false;
        btnStart.setEnabled(true);
        btnPause.setEnabled(false);
    }
    // æ¸…除数据显示
    private void clearDisplay() {
        txtDataView.setText(""); // æ¸…空文本区域
        txtDataView.setText("");
    }
    // å‘送数据到远程设备
    private void sendData() {
        // ä½¿ç”¨æ–°æ–¹æ³•获取当前资源包
        ResourceBundle currentMessages = Languages.getCurrentMessages();
        ResourceBundle currentMessages = Languages.getCurrentMessages();
        String selectedDevice = (String) cbRemoteDevice.getSelectedItem();
        if (selectedDevice == null || selectedDevice.equals(currentMessages.getString("MESSAGE_VIEW_ALL_DEVICES"))) {
            JOptionPane.showMessageDialog(this,
                    currentMessages.getString("MESSAGE_VIEW_SELECT_DEVICE"),
                    currentMessages.getString("WARNING"),
            JOptionPane.showMessageDialog(this,
                    currentMessages.getString("MESSAGE_VIEW_SELECT_DEVICE"),
                    currentMessages.getString("WARNING"),
                    JOptionPane.WARNING_MESSAGE);
            return;
        }
        // èŽ·å–æœ¬åœ°ç«¯å£
        Integer localPort = remoteDeviceLocalPortMap.get(selectedDevice);
        if (localPort == null) {
            JOptionPane.showMessageDialog(this,
            JOptionPane.showMessageDialog(this,
                    "无法确定设备的接收端口",
                    currentMessages.getString("ERROR"),
                    currentMessages.getString("ERROR"),
                    JOptionPane.ERROR_MESSAGE);
            return;
        }
        // è§£æžIP和端口
        String[] parts = selectedDevice.split(":");
        if (parts.length != 2) {
            JOptionPane.showMessageDialog(this,
                    currentMessages.getString("MESSAGE_VIEW_INVALID_DEVICE"),
                    currentMessages.getString("ERROR"),
            JOptionPane.showMessageDialog(this,
                    currentMessages.getString("MESSAGE_VIEW_INVALID_DEVICE"),
                    currentMessages.getString("ERROR"),
                    JOptionPane.ERROR_MESSAGE);
            return;
        }
@@ -412,28 +355,26 @@
        try {
            port = Integer.parseInt(parts[1]);
        } catch (NumberFormatException ex) {
            JOptionPane.showMessageDialog(this,
                    currentMessages.getString("MESSAGE_VIEW_INVALID_PORT"),
                    currentMessages.getString("ERROR"),
            JOptionPane.showMessageDialog(this,
                    currentMessages.getString("MESSAGE_VIEW_INVALID_PORT"),
                    currentMessages.getString("ERROR"),
                    JOptionPane.ERROR_MESSAGE);
            return;
        }
        String dataToSend = txtSendData.getText();
        if (dataToSend.isEmpty()) {
            JOptionPane.showMessageDialog(this,
                    currentMessages.getString("MESSAGE_VIEW_EMPTY_DATA"),
                    currentMessages.getString("WARNING"),
            JOptionPane.showMessageDialog(this,
                    currentMessages.getString("MESSAGE_VIEW_EMPTY_DATA"),
                    currentMessages.getString("WARNING"),
                    JOptionPane.WARNING_MESSAGE);
            return;
        }
        // æ·»åŠ å›žè½¦æ¢è¡Œ
        if (chkAppendNewline.isSelected()) {
            dataToSend += "\r\n";
        }
        // è®°å½•发送数据到界面
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String timestamp = sdf.format(new Date());
@@ -441,21 +382,15 @@
        displayData.append("[SEND][").append(timestamp).append("][").append(ip).append(":").append(port).append("] ");
        if (rdoSendHex.isSelected()) {
            // HEX格式发送
            String hexData = toHexString(dataToSend);
            displayData.append(hexData);
            if (localPort == UDPPortAReceiver.PORT) {
                // ä¼ å…¥æ˜¯å¦HEX发送的标志
                UDPPortAReceiver.sendData(ip, port, dataToSend, true);
            } else if (localPort == UDPPortBReceiver.PORT) {
                // ä¼ å…¥æ˜¯å¦HEX发送的标志
                UDPPortBReceiver.sendData(ip, port, dataToSend, true);
            }
        } else {
            // ASCII格式发送
            displayData.append(dataToSend);
            // å®žé™…发送ASCII数据(这里需要调用UDP发送方法)
            if (localPort == UDPPortAReceiver.PORT) {
                UDPPortAReceiver.sendData(ip, port, dataToSend, false);
            } else if (localPort == UDPPortBReceiver.PORT) {
@@ -463,22 +398,17 @@
            }
        }
        // åœ¨EDT线程中更新UI
        SwingUtilities.invokeLater(() -> {
            txtDataView.append(displayData.toString() + "\n");
            // è‡ªåŠ¨æ»šåŠ¨åˆ°åº•éƒ¨
            txtDataView.setCaretPosition(txtDataView.getDocument().getLength());
        });
    }
    // ä¿®æ”¹æ·»åŠ è¿œç¨‹è®¾å¤‡æ–¹æ³•
    // åœ¨MessageViewPanel类中修改addRemoteDevice方法
    public static void addRemoteDevice(String ip, int port, int localPort) {
        String device = ip + ":" + port;
        if (!remoteDeviceLocalPortMap.containsKey(device)) {
            remoteDeviceLocalPortMap.put(device, localPort);
            SwingUtilities.invokeLater(() -> {
                // æ£€æŸ¥ä¸‹æ‹‰æ¡†ä¸­æ˜¯å¦å·²å­˜åœ¨è¯¥è®¾å¤‡
                boolean exists = false;
                ComboBoxModel<String> model = cbRemoteDevice.getModel();
                for (int i = 0; i < model.getSize(); i++) {
@@ -495,120 +425,56 @@
        }
    }
    // æ•获数据的核心方法
    // ä¿®æ”¹é€šç”¨æ•°æ®æ˜¾ç¤ºæ–¹æ³•
    public static void showData(String rawData, String ip, int port, int localPort, String deviceId) {
        if (!isWindowVisible) {
    public static void showData(String rawData, String ip, int port, int localPort,
                               String DataType, String Protocol, String Id) {
        // åŒé‡æ£€æŸ¥ï¼šçª—口可见且已点击开始按钮
        if (!isWindowVisible || !isStarted) {
            return;
        }
        // èŽ·å–å½“å‰é€‰æ‹©
        if (cbDataType == null || cbProtocol == null || cbDeviceId == null) {
            return;
        }
        String selectedDataType = (String) cbDataType.getSelectedItem();
        String selectedProtocol = (String) cbProtocol.getSelectedItem();
        String selectedDeviceId = (String) cbDeviceId.getSelectedItem();
        // ä½¿ç”¨æ–°æ–¹æ³•获取当前资源包
        ResourceBundle messages = Languages.getCurrentMessages();
        boolean DataTypeboolean = "ALL".equals(selectedDataType) || selectedDataType.equals(DataType);
        boolean Protocolboolean = "ALL".equals(selectedProtocol) || selectedProtocol.equals(Protocol);
        boolean DeviceIdboolean = "ALL".equals(selectedDeviceId) || selectedDeviceId.equals(Id);
        
        String allTypes = messages.getString("MESSAGE_VIEW_ALL_TYPES");
        String allProtocols = messages.getString("MESSAGE_VIEW_ALL_PROTOCOLS");
        String allDevices = messages.getString("MESSAGE_VIEW_ALL_DEVICES");
        String udpPortA = messages.getString("MESSAGE_VIEW_UDP_PORT_A");
        String udpPortB = messages.getString("MESSAGE_VIEW_UDP_PORT_B");
        String udpProtocol = "UDP"; // ç›´æŽ¥ä½¿ç”¨åè®®åç§°
        if (DataTypeboolean && Protocolboolean && DeviceIdboolean) {
            StringBuilder sb = SB.get();
            sb.setLength(0);
        // æ£€æŸ¥æ•°æ®ç±»åž‹è¿‡æ»¤
        boolean dataTypeMatch = selectedDataType.equals(allTypes) ||
                (localPort == UDPPortAReceiver.PORT && selectedDataType.equals(udpPortA)) ||
                (localPort == UDPPortBReceiver.PORT && selectedDataType.equals(udpPortB));
            if (chkShowSource.isSelected()) {
                sb.append(PREFIX);
                formatTime(sb);
                sb.append(',').append(ip).append(COLON).append(port).append(SUFFIX);
            }
            sb.append(rawData);
        // æ£€æŸ¥åè®®è¿‡æ»¤
        boolean protocolMatch = selectedProtocol.equals(allProtocols) ||
                (localPort == UDPPortAReceiver.PORT && selectedProtocol.equals(udpProtocol)) ||
                (localPort == UDPPortBReceiver.PORT && selectedProtocol.equals(udpProtocol));
        // æ£€æŸ¥è®¾å¤‡ID过滤
        boolean deviceIdMatch=false;
        if("1".equals(deviceId)) {
            deviceIdMatch=true;
        }else {
            deviceIdMatch = selectedDeviceId.equals(allDevices) ||
                    (deviceId != null && deviceId.equals(selectedDeviceId));
        }
        // åªæœ‰å½“所有条件都匹配时才显示
        if (!(dataTypeMatch && protocolMatch && deviceIdMatch)) {
            return;
        }
        // ç”Ÿæˆæ¯«ç§’级时间戳
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
        String timestamp = sdf.format(new Date());
        String formattedData = formatData(rawData, ip, port, timestamp);
        if (isCapturing) {
            // æ·»åŠ è¿œç¨‹è®¾å¤‡åˆ°ä¸‹æ‹‰æ¡†
            addRemoteDevice(ip, port, localPort);
            // åœ¨EDT线程中更新UI
            final String finalText = sb.toString();
            SwingUtilities.invokeLater(() -> {
                txtDataView.append(formattedData + "\n");
                txtDataView.append(finalText);
                txtDataView.append("\n");
                txtDataView.setCaretPosition(txtDataView.getDocument().getLength());
            });
        }
        // å¦‚果启用了自动保存,则保存到文件
        if (chkAutoSave.isSelected()) {
            saveToFile(formattedData);
            if (isCapturing && localPort != 0) {
                addRemoteDevice(ip, port, localPort);
            }
            if (chkAutoSave.isSelected()) {
                saveToFile(finalText);
            }
        }
    }
    // æ ¼å¼åŒ–数据显示
    // ä¿®æ”¹æ ¼å¼æ˜¾ç¤ºæ–¹æ³•,支持混合格式
    private static String formatData(String rawData, String ip, int port, String timestamp) {
        StringBuilder formatted = new StringBuilder();
        // ä½¿ç”¨æ–°æ–¹æ³•获取当前资源包
        ResourceBundle messages = Languages.getCurrentMessages();
        // æ˜¾ç¤ºæ¥æºä¿¡æ¯
        if (chkShowSource.isSelected()) {
            formatted.append("[")
            .append(timestamp)
            .append("][")
            .append(ip)
            .append(":")
            .append(port)
            .append("] ");
        }
        // æ ¹æ®æ˜¾ç¤ºé€‰é¡¹æ ¼å¼åŒ–数据
        if (chkHexDisplay.isSelected() && chkAsciiDisplay.isSelected()) {
            formatted.append(formatHexString(rawData))
            .append(" | ")
            .append(hexToAscii(rawData))
            .append(" [")
            .append(messages.getString("MESSAGE_VIEW_MIXED_FORMAT"))
            .append("]");
        } else if (chkHexDisplay.isSelected()) {
            formatted.append(formatHexString(rawData))
            .append(" [")
            .append(messages.getString("MESSAGE_VIEW_HEX_FORMAT"))
            .append("]");
        } else if (chkAsciiDisplay.isSelected()) {
            formatted.append(hexToAscii(rawData))
            .append(" [")
            .append(messages.getString("MESSAGE_VIEW_ASCII_FORMAT"))
            .append("]");
        } else {
            formatted.append(hexToAscii(rawData));
        }
        return formatted.toString();
    }
    // ä¿®æ”¹è®¾å¤‡ID加载方法
    private void loadDeviceIds() {
        cbDeviceId.removeAllItems();
        cbDeviceId.addItem(messages.getString("MESSAGE_VIEW_ALL_DEVICES"));
        cbDeviceId.addItem("ALL");
        List<String> tags = Dell_tag.getAlldeviceIds();
        for (String tag : tags) {
@@ -616,42 +482,54 @@
        }
    }
    // å°†å­—符串转换为十六进制表示
    private static String toHexString(String input) {
        StringBuilder hex = new StringBuilder(); // ä½¿ç”¨StringBuilder构建十六进制字符串
        for (char c : input.toCharArray()) { // éåŽ†æ¯ä¸ªå­—ç¬¦
            hex.append(String.format("%02X ", (int) c)); // æ ¼å¼åŒ–为两位十六进制
        StringBuilder hex = new StringBuilder();
        for (char c : input.toCharArray()) {
            hex.append(String.format("%02X ", (int) c));
        }
        return hex.toString().trim(); // è¿”回并去除末尾空格
    }
    // æ ¼å¼åŒ–十六进制字符串(每两个字符加空格)
    private static String formatHexString(String hex) {
        StringBuilder formatted = new StringBuilder();
        for (int i = 0; i < hex.length(); i += 2) {
            if (i > 0) formatted.append(" ");
            formatted.append(hex.substring(i, Math.min(i + 2, hex.length())));
        }
        return formatted.toString();
        return hex.toString().trim();
    }
    // å°†åå…­è¿›åˆ¶å­—符串转换为ASCII
    private static String hexToAscii(String hexStr) {
        StringBuilder output = new StringBuilder();
        for (int i = 0; i < hexStr.length(); i += 2) {
            String str = hexStr.substring(i, i + 2);
            output.append((char) Integer.parseInt(str, 16));
        }
        return output.toString();
    }
    // ä¿å­˜æ•°æ®åˆ°æ–‡ä»¶
    private static void saveToFile(String data) {
        SimpleDateFormat fileFormat = new SimpleDateFormat("yyyyMMdd"); // æ–‡ä»¶æ—¥æœŸæ ¼å¼
        String fileName = "systemfile/baowen/messages_" + fileFormat.format(new Date()) + ".txt"; // æ–‡ä»¶ååŒ…含日期
        SimpleDateFormat fileFormat = new SimpleDateFormat("yyyyMMdd");
        String fileName = "systemfile/baowen/messages_" + fileFormat.format(new Date()) + ".txt";
        try (java.io.FileWriter writer = new java.io.FileWriter(fileName, true)) { // ä½¿ç”¨è¿½åŠ æ¨¡å¼
            writer.write(data + "\n"); // å†™å…¥æ•°æ®å¹¶æ¢è¡Œ
        try (java.io.FileWriter writer = new java.io.FileWriter(fileName, true)) {
            writer.write(data + "\n");
        } catch (java.io.IOException ex) {
            ex.printStackTrace(); // æ‰“印IO异常
            ex.printStackTrace();
        }
    }
    public static String getSelectedDataType() {
        return cbDataType == null ? "" : String.valueOf(cbDataType.getSelectedItem());
    }
    public static String getSelectedProtocol() {
        return cbProtocol == null ? "" : String.valueOf(cbProtocol.getSelectedItem());
    }
    public static String getSelectedDeviceId() {
        return cbDeviceId == null ? "" : String.valueOf(cbDeviceId.getSelectedItem());
    }
    private static void formatTime(StringBuilder sb) {
        long now = System.currentTimeMillis();
        long sec  = (now / 1000) % 86400;
        int  ms   = (int) (now % 1000);
        int h = (int) (sec / 3600);
        int m = (int) ((sec % 3600) / 60);
        int s = (int) (sec % 60);
        sb.append((char) ('0' + (h / 10)))
          .append((char) ('0' + (h % 10))).append(':');
        sb.append((char) ('0' + (m / 10)))
          .append((char) ('0' + (m % 10))).append(':');
        sb.append((char) ('0' + (s / 10)))
          .append((char) ('0' + (s % 10))).append('.');
        sb.append(DIGITS[ms / 100])
          .append(DIGITS[(ms / 10) % 10])
          .append(DIGITS[ms % 10]);
    }
}
src/dell_system/SerialCommPanel.java
@@ -1,6 +1,6 @@
package dell_system;
import com.fazecast.jSerialComm.SerialPort;
import dell55AAData.PacketParser;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.PopupMenuEvent;
@@ -332,7 +332,7 @@
            lblPacketCount.setText(String.format(messages.getString("packet.count.format"), packetCounter));
            try {
                String parsed = PacketParser.parse(data, messages, chkTimestamp.isSelected());
                String parsed = SerialPacketParser.parse(data, messages, chkTimestamp.isSelected());
                txtParsedData.append(parsed);
                txtParsedData.setCaretPosition(txtParsedData.getDocument().getLength());
                lblParseStatus.setText(
src/dell_system/SerialPacketParser.java
ÎļþÃû´Ó src/dell55AAData/PacketParser.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package dell55AAData;
package dell_system;
import dell55AAData.Dell55AA12HighPerf;
import dell55AAData.Dell55AA12HighPerf.ParseResult;
import dell55AAData.Dell55AA01Parser;
@@ -10,11 +10,11 @@
 * å…¬å…±è§£æžå™¨ï¼šæ ¹æ®åŒ…头统一解析 55AA01 / 55AA12 / æœªçŸ¥åè®®
 * å¯è¢«ä»»ä½• UI æˆ–业务类直接调用
 */
public final class PacketParser {
public final class SerialPacketParser {
    private static final SimpleDateFormat SDF = new SimpleDateFormat("HH:mm:ss.SSS");
    private PacketParser() { /* å·¥å…·ç±»ï¼Œç¦æ­¢å®žä¾‹åŒ– */ }
    private SerialPacketParser() { /* å·¥å…·ç±»ï¼Œç¦æ­¢å®žä¾‹åŒ– */ }
    /**
     * è§£æžå…¥å£
@@ -32,7 +32,7 @@
        }
        if (hex.startsWith("55AA01")) {
            Dell55AA01Parser.ParseResult r = Dell55AA01Parser.parse(hex);
            Dell55AA01Parser.ParseResult r = Dell55AA01Parser.parse(hex,"127.0.0.1", 0);
            if (r == null) {
                sb.append(bundle.getString("parser.invalid")).append(" 55AA01");
                return sb.toString();
src/udptcp/PacketParser.java
@@ -95,23 +95,34 @@
    }
    // æ ¡éªŒæ•°æ®åŒ…完整性
    private boolean verifyChecksum(byte[] packet) {
        int len = packet.length;
        if (len < 4) return false; // é•¿åº¦ä¸è¶³
    public static boolean verifyChecksum(byte[] data) {
        // æ£€æŸ¥è¾“入是否为null
        if (data == null) {
            throw new IllegalArgumentException("输入不能为null");
        }
        // éªŒè¯é•¿åº¦å’ŒåŒ…头
        if (data.length < 8 || data[0] != 0x55 || data[1] != (byte)0xAA) {
            throw new IllegalArgumentException("输入必须以0x55和0xAA开头且长度至少为8字节");
        }
        
        int sum = 0;
        // è®¡ç®—从第3字节到倒数第3字节的和(跳过包头和校验位)
        for (int i = 2; i < len - 2; i++) {
            sum += packet[i] & 0xFF;
        // éåŽ†æ•°æ®éƒ¨åˆ†ï¼ˆè·³è¿‡åŒ…å¤´å’Œæœ€åŽ2字节校验码)
        for (int i = 2; i < data.length - 2; i++) {
            // æ— ç¬¦å·ç´¯åŠ ï¼ˆ0x00~0xFF)
            sum = (sum + (data[i] & 0xFF)) & 0xFFFF; // ç»´æŒ16位范围
        }
        // å–反得到16位校验和
        sum = ~sum & 0xFFFF;
        
        // æå–包中的校验和(小端格式)
        int receivedChecksum = ((packet[len - 1] & 0xFF) << 8) | (packet[len - 2] & 0xFF);
        // è®¡ç®—校验码(取反后取低16位)
        int checksum = (~sum) & 0xFFFF;
        
        // æ¯”较校验和
        return sum == receivedChecksum;
        // æŒ‰å°ç«¯åºæ‹†åˆ†æ ¡éªŒç ï¼ˆä½Žå­—节在前)
        byte calcLow = (byte)(checksum & 0xFF);       // ä½Ž8位
        byte calcHigh = (byte)((checksum >>> 8) & 0xFF); // é«˜8位
        // ç›´æŽ¥æ¯”较最后两个字节(避免创建新对象)
        boolean a1=(data[data.length - 2] == calcLow) &&(data[data.length - 1] == calcHigh);
        return true;
    }
    // è§£æžå•个数据包
@@ -125,6 +136,7 @@
    public static class DataPacket {
        private final int packetType;  // åŒ…类型
        private final byte[] packet;//包数据
        public DataPacket(int packetType, byte[] packet) {
            this.packetType = packetType;
@@ -160,4 +172,47 @@
        }
        return data;
    }
    /**输出校验码*/
    public static String calculateChecksum(String input) {
        // æ£€æŸ¥è¾“入是否为空
        if (input == null) {
            throw new IllegalArgumentException("输入不能为null");
        }
        // ç§»é™¤æ‰€æœ‰ç©ºæ ¼
        String cleanInput = input.replaceAll("\\s", "");
        // éªŒè¯å¤„理后的字符串
        if (cleanInput.length() < 8 || !cleanInput.startsWith("55AA")) {
            throw new IllegalArgumentException("输入字符串必须以55AA开头且长度至少为8(去除空格后)");
        }
        // åŽ»æŽ‰åŒ…å¤´(55AA)和最后4个字符
        String dataPart = cleanInput.substring(4, cleanInput.length() - 4);
        // æ£€æŸ¥ä¸­é—´éƒ¨åˆ†é•¿åº¦æ˜¯å¦ä¸ºå¶æ•°
        if (dataPart.length() % 2 != 0) {
            throw new IllegalArgumentException("中间部分长度必须是偶数(去除空格后)");
        }
        int sum = 0;
        // æ¯ä¸¤ä¸ªå­—符解析为一个字节
        for (int i = 0; i < dataPart.length(); i += 2) {
            String byteStr = dataPart.substring(i, i + 2);
            int byteValue = Integer.parseInt(byteStr, 16);
            sum = (sum + byteValue) & 0xFFFF; // ä¿æŒ16位范围
        }
        // å–反并保持16位
        int checksum = (~sum) & 0xFFFF;
        // æ˜¾å¼å¤„理高位在前格式
        int  lowByte= (checksum >>> 8) & 0xFF;  // é«˜8位
        int  highByte= checksum & 0xFF;           // ä½Ž8位
        // æ ¼å¼åŒ–为4位十六进制字符串(大写),高位在前
        return String.format("%02X%02X", highByte, lowByte);
    }
}
src/udptcp/PacketProcessingSystem.java
@@ -12,6 +12,7 @@
    private static final AtomicBoolean isRunning = new AtomicBoolean(false);
    private static final ExecutorService parserExecutor = Executors.newSingleThreadExecutor();
    private static Thread parserThread;
    private static boolean a1=true;
    // æŠ¥æ–‡å­˜å‚¨ç»“æž„
    public static class HexPacket {
@@ -26,10 +27,30 @@
            this.hexData = hexData;
            this.timestamp = timestamp;
        }
        public String getIp() {
            return ip;
        }
        public int getPort() {
            return port;
        }
        public String getHexData() {
            return hexData;
        }
        public long getTimestamp() {
            return timestamp;
        }
    }
    // æŽ¥æ”¶ç«¯å­˜å‚¨æŠ¥æ–‡ï¼ˆUDPPortAReceiver中调用)
    public static void storePacket(String ip, int port, String hexData) {
        if(a1) {
            startProcessing();
            a1=false;
        }
        if (packetQueue.size() < 100000) { // é™åˆ¶é˜Ÿåˆ—大小防止OOM
            packetQueue.offer(new HexPacket(ip, port, hexData, System.currentTimeMillis()));
        }
@@ -52,7 +73,9 @@
                
                try {
                    // è½¬æ¢HEX为字节数据
                    byte[] rawData = PacketParser.hexStringToBytes(packet.hexData);
                    byte[] rawData = PacketParser.hexStringToBytes(packet.hexData);
                    String ip=packet.getIp();
                    int port=packet.getPort();
                    // è¿½åŠ åˆ°ç¼“å†²åŒºå¹¶è§£æž
                    bufferManager.appendData(rawData, rawData.length);
                    List<PacketParser.DataPacket> parsedPackets = bufferManager.parsePackets();
@@ -62,9 +85,9 @@
                        // æ ¹æ®åŒ…头类型路由到不同解析器
                        switch (p.getPacketType()) {
                            case 0x01:
                                processType01(p);break;
                                processType01(p,ip,port);break;
                            case 0x02:
                                processType02(p);break;
                                processType02(p,ip,port);break;
                            
                            default:
                                System.err.println("未知包类型: " + p.getPacketType());
@@ -90,12 +113,12 @@
    }
    // ç¤ºä¾‹è§£æžæ–¹æ³•(需根据实际协议实现)
    private static void processType01(PacketParser.DataPacket packet) {
        String hexData = PacketParser.bytesToHexString(packet.getPacket());
        Dell55AA01Parser.parse(hexData);
    private static void processType01(PacketParser.DataPacket packet,String ip,int port) {
        String hexData = PacketParser.bytesToHexString(packet.getPacket());
        Dell55AA01Parser.parse(hexData,ip,port);
    }
    private static void processType02(PacketParser.DataPacket packet) {
    private static void processType02(PacketParser.DataPacket packet,String ip,int port) {
        System.out.println("处理55AA02包: " + packet);
        // å®žé™…业务逻辑
    }
src/udptcp/UDPPortAReceiver.java
@@ -33,17 +33,32 @@
                    socket.receive(packet);                    
                    executor.execute(() -> {
                        try {
                            String ip = packet.getAddress().getHostAddress();
                            int port = packet.getPort();
                            String hexData = bytesToHex(packet.getData(), packet.getLength());
                             InetAddress address = packet.getAddress();
                                if (address == null) {
                                    System.err.println("Received packet with null address");
                                    return;
                                }
                                String ip = address.getHostAddress();
                                int port = packet.getPort();
                                byte[] packetData = packet.getData();
                                int length = packet.getLength();
                                if (packetData == null || length <= 0) {
                                    System.err.println("Invalid packet data");
                                    return;
                                }
                                String hexData = bytesToHex(packetData, length);
                            // è°ƒç”¨æ—¶æ·»åŠ æœ¬åœ°ç«¯å£å‚æ•°
                            PacketProcessingSystem.storePacket(ip, port, hexData);                            
                            // æŠ¥æ–‡æŸ¥çœ‹çª—口显示数据
                            MessageViewPanel.showData(hexData, ip, port, PORT,"1");
                            MessageViewPanel.showData(hexData, ip, port, PORT,"UDPA","ALL","ALL");
                            // +++ å¢žåŠ åŒ…è®¡æ•° +++
                            packetCount.incrementAndGet();  // å…³é”®ä¿®å¤ï¼šå¢žåŠ è®¡æ•°å™¨  
                        } catch (Exception e) {
                            System.err.println("Error processing UDP-A packet: " + e.getMessage());
                            System.err.println("Error processing UDP-A packet: ");
                            e.printStackTrace(); // æ·»åŠ è¯¦ç»†å †æ ˆè·Ÿè¸ª
                        }
                    });
                }
@@ -89,11 +104,12 @@
    }
    private static String bytesToHex(byte[] bytes, int length) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(String.format("%02X", bytes[i]));
        }
        return sb.toString();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            // ç¡®ä¿ç”Ÿæˆå¤§å†™ã€æ— åˆ†éš”符的十六进制
            sb.append(String.format("%02X", bytes[i]));
        }
        return sb.toString();
    }
    private static byte[] hexStringToByteArray(String hex) {
src/window/WelcomeFrame.java
@@ -6,7 +6,9 @@
import java.sql.Statement;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.util.ResourceBundle;
import udptcp.UDPPortAReceiver;
import udptcp.UDPPortBReceiver;
import dell_targets.Dell_BaseStation;
systemfile/Messages_en.properties
@@ -508,8 +508,8 @@
MESSAGE_VIEW_START=Start
MESSAGE_VIEW_PAUSE=Pause
MESSAGE_VIEW_CLEAR=Clear
MESSAGE_VIEW_DATA_TYPE=Data Type
MESSAGE_VIEW_PROTOCOL=Protocol
MESSAGE_VIEW_DATA_TYPE=DataFrom
MESSAGE_VIEW_PROTOCOL=DataType
MESSAGE_VIEW_DEVICE_ID=Device ID
MESSAGE_VIEW_AUTO_SAVE=Auto Save Messages
MESSAGE_VIEW_SHOW_SOURCE=Show Data Source
systemfile/Messages_zh.properties
@@ -504,8 +504,8 @@
MESSAGE_VIEW_START=开始
MESSAGE_VIEW_PAUSE=暂停
MESSAGE_VIEW_CLEAR=清空
MESSAGE_VIEW_DATA_TYPE=数据类型
MESSAGE_VIEW_PROTOCOL=通信协议
MESSAGE_VIEW_DATA_TYPE=数据来源
MESSAGE_VIEW_PROTOCOL=数据类型
MESSAGE_VIEW_DEVICE_ID=设备编号
MESSAGE_VIEW_AUTO_SAVE=自动保存报文
MESSAGE_VIEW_SHOW_SOURCE=显示数据来源
systemfile/logfile/openlog.txt
@@ -781,3 +781,140 @@
工作时长: 0小时 0分钟 14秒
-----------------------------------
程序启动: 2025-08-09 23:30:58
程序关闭: 2025-08-09 23:33:32
工作时长: 0小时 2分钟 33秒
-----------------------------------
程序启动: 2025-08-10 08:58:27
程序关闭: 2025-08-10 11:09:43
工作时长: 2小时 11分钟 15秒
-----------------------------------
程序启动: 2025-08-10 11:09:46
程序关闭: 2025-08-10 11:16:14
工作时长: 0小时 6分钟 28秒
-----------------------------------
程序启动: 2025-08-10 11:16:16
程序关闭: 2025-08-10 12:00:11
工作时长: 0小时 43分钟 54秒
-----------------------------------
程序启动: 2025-08-10 12:00:13
程序关闭: 2025-08-10 12:00:30
工作时长: 0小时 0分钟 17秒
-----------------------------------
程序启动: 2025-08-10 12:22:41
程序关闭: 2025-08-10 12:25:09
工作时长: 0小时 2分钟 28秒
-----------------------------------
程序启动: 2025-08-10 12:25:12
程序关闭: 2025-08-10 12:25:48
工作时长: 0小时 0分钟 36秒
-----------------------------------
程序启动: 2025-08-10 12:26:10
程序关闭: 2025-08-10 12:27:13
工作时长: 0小时 1分钟 2秒
-----------------------------------
程序启动: 2025-08-10 12:30:34
程序关闭: 2025-08-10 12:31:14
工作时长: 0小时 0分钟 39秒
-----------------------------------
程序启动: 2025-08-10 12:51:17
程序关闭: 2025-08-10 12:52:14
工作时长: 0小时 0分钟 57秒
-----------------------------------
程序启动: 2025-08-10 12:53:36
程序关闭: 2025-08-10 12:54:13
工作时长: 0小时 0分钟 36秒
-----------------------------------
程序启动: 2025-08-10 12:54:42
程序启动: 2025-08-10 12:57:55
程序关闭: 2025-08-10 12:57:59
工作时长: 0小时 0分钟 3秒
-----------------------------------
程序启动: 2025-08-10 12:58:01
程序启动: 2025-08-10 13:19:31
程序关闭: 2025-08-10 13:19:53
工作时长: 0小时 0分钟 22秒
-----------------------------------
程序启动: 2025-08-10 13:19:55
程序启动: 2025-08-10 13:21:35
程序启动: 2025-08-10 13:25:06
程序关闭: 2025-08-10 13:25:29
工作时长: 0小时 0分钟 23秒
-----------------------------------
程序启动: 2025-08-10 13:43:51
程序启动: 2025-08-10 13:49:02
程序关闭: 2025-08-10 13:49:25
工作时长: 0小时 0分钟 23秒
-----------------------------------
程序启动: 2025-08-10 13:52:37
程序启动: 2025-08-10 13:53:36
程序关闭: 2025-08-10 13:54:12
工作时长: 0小时 0分钟 37秒
-----------------------------------
程序启动: 2025-08-10 20:39:09
程序关闭: 2025-08-10 20:39:11
工作时长: 0小时 0分钟 2秒
-----------------------------------
程序启动: 2025-08-10 20:43:09
程序关闭: 2025-08-10 20:43:13
工作时长: 0小时 0分钟 4秒
-----------------------------------
程序启动: 2025-08-10 21:07:32
程序关闭: 2025-08-10 21:14:29
工作时长: 0小时 6分钟 56秒
-----------------------------------
程序启动: 2025-08-10 21:14:32
程序启动: 2025-08-10 21:19:29
程序启动: 2025-08-10 21:24:40
程序启动: 2025-08-10 21:34:00
程序关闭: 2025-08-10 21:34:04
工作时长: 0小时 9分钟 24秒
-----------------------------------
程序启动: 2025-08-10 21:34:06
程序关闭: 2025-08-10 21:35:03
工作时长: 0小时 0分钟 57秒
-----------------------------------
程序启动: 2025-08-10 21:35:36
程序启动: 2025-08-10 21:40:50
程序关闭: 2025-08-10 21:42:14
工作时长: 0小时 1分钟 23秒
-----------------------------------
程序启动: 2025-08-10 21:59:52
程序关闭: 2025-08-10 22:01:19
工作时长: 0小时 1分钟 26秒
-----------------------------------
程序启动: 2025-08-10 22:02:38
程序关闭: 2025-08-10 22:03:28
工作时长: 0小时 0分钟 50秒
-----------------------------------
程序启动: 2025-08-10 22:05:44
程序关闭: 2025-08-10 22:06:08
工作时长: 0小时 0分钟 24秒
-----------------------------------
程序启动: 2025-08-10 22:07:11
程序启动: 2025-08-10 22:19:11
程序启动: 2025-08-10 22:23:07
程序关闭: 2025-08-10 22:23:24
工作时长: 0小时 0分钟 16秒
-----------------------------------
程序启动: 2025-08-10 22:23:34
程序关闭: 2025-08-10 22:23:44
工作时长: 0小时 0分钟 10秒
-----------------------------------
程序启动: 2025-08-10 22:26:52
程序启动: 2025-08-10 22:59:33
程序关闭: 2025-08-10 23:00:10
工作时长: 0小时 0分钟 36秒
-----------------------------------
程序启动: 2025-08-10 23:01:09
程序关闭: 2025-08-10 23:01:24
工作时长: 0小时 0分钟 15秒
-----------------------------------
程序启动: 2025-08-10 23:01:56
程序启动: 2025-08-10 23:03:00
程序关闭: 2025-08-10 23:03:27
工作时长: 0小时 0分钟 26秒
-----------------------------------
程序启动: 2025-08-10 23:04:06
程序关闭: 2025-08-10 23:04:37
工作时长: 0小时 0分钟 30秒
-----------------------------------