826220679@qq.com
2025-08-07 0e0e51fc63a865977d751271be28437e24dd6a99
修改
已删除5个文件
已添加8个文件
已修改5个文件
1389 ■■■■■ 文件已修改
lib/RXTXcomm.jar 补丁 | 查看 | 原始文档 | blame | 历史
lib/jSerialComm-2.10.4.jar 补丁 | 查看 | 原始文档 | blame | 历史
src/Dell55aa/Dell55AA01.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Dell55aa/Dell55AA12HighPerf.java 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dell_system/SerialCommPanel.java 586 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dell_system/SerialPortService.java 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/publicsWay/JugeNumber.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/publicsWay/UwbDataParser.java 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/window/ContentPanelFactory.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/window/NavigationTreeFactory.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/20250724log.txt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/20250726log.txt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/20250805log.txt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/20250806log.txt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/20250807log.txt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/Messages_en.properties 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/Messages_zh.properties 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/logfile/openlog.txt 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/RXTXcomm.jar
Binary files differ
lib/jSerialComm-2.10.4.jar
Binary files differ
src/Dell55aa/Dell55AA01.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
package Dell55aa;
public class Dell55AA01 {
}
src/Dell55aa/Dell55AA12HighPerf.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,180 @@
package Dell55aa;
public class Dell55AA12HighPerf { // é«˜æ€§èƒ½è§£æžç±»
    // å¸¸é‡å®šä¹‰ï¼ˆé¿å…é‡å¤åˆ›å»ºï¼‰
    private static final String HEADER = "55AA12"; // åŒ…头常量
    private static final int MIN_LENGTH = 34; // æœ€å°æœ‰æ•ˆé•¿åº¦
    private static final ThreadLocal<ParseResult> RESULT_CACHE = // çº¿ç¨‹æœ¬åœ°ç»“果缓存
        ThreadLocal.withInitial(ParseResult::new);
    private static final ThreadLocal<char[]> CHAR_BUF_CACHE = // å­—符缓冲区缓存
        ThreadLocal.withInitial(() -> new char[256]);
    // åå…­è¿›åˆ¶å­—符转换表(避免重复计算)
    private static final int[] HEX_VALUES = new int[128];
    static {
        for (int i = 0; i < HEX_VALUES.length; i++) {
            char c = (char) i;
            if (c >= '0' && c <= '9') HEX_VALUES[i] = c - '0';
            else if (c >= 'A' && c <= 'F') HEX_VALUES[i] = 10 + (c - 'A');
            else if (c >= 'a' && c <= 'f') HEX_VALUES[i] = 10 + (c - 'a');
            else HEX_VALUES[i] = -1; // éžæ³•字符标记
        }
    }
    // è§£æžç»“果复用类(对象池模式)
    public static class ParseResult {
        public int tagId;
        public int sequenceNum;
        public int power;
        public int vibrationState;
        public boolean tagRemoved;
        public boolean isSleeping;
        public boolean isStatic;
        public boolean sosButtonPressed;
        public int tagHeight;
        public int anchorCount;
        public int[] anchorIds = new int[0];
        public int[] distances = new int[0];
        public int[] anchorPowers = new int[0];
        public int[] signalStrengths1 = new int[0];
        public int[] signalStrengths2 = new int[0];
        // é‡ç½®æ–¹æ³•(避免重新创建数组)
        public void reset() {
            tagId = 0;
            sequenceNum = 0;
            power = 0;
            vibrationState = 0;
            tagRemoved = false;
            isSleeping = false;
            isStatic = false;
            sosButtonPressed = false;
            tagHeight = 0;
            anchorCount = 0;
            // ä¿ç•™æ•°ç»„,仅重置长度
        }
    }
    /**
     * é«˜æ€§èƒ½è§£æžæ–¹æ³•
     * @param message åŽŸå§‹16进制消息
     * @return è§£æžç»“果对象(线程内复用)
     */
    public static ParseResult parse(String message) {
        // å¿«é€Ÿé•¿åº¦æ ¡éªŒ
        if (message == null || message.length() < MIN_LENGTH) {
            return null; // å¿«é€Ÿå¤±è´¥
        }
        // åŒ…头检查(避免创建子字符串)
        if (!(message.charAt(0) == '5' &&
              message.charAt(1) == '5' &&
              message.charAt(2) == 'A' &&
              message.charAt(3) == 'A' &&
              message.charAt(4) == '1' &&
              message.charAt(5) == '2')) {
            return null; // åŒ…头无效
        }
        // ä»Žçº¿ç¨‹ç¼“存获取结果对象
        ParseResult result = RESULT_CACHE.get();
        result.reset(); // é‡ç½®å¯¹è±¡çŠ¶æ€
        // ç›´æŽ¥æ“ä½œå­—符数组(避免字符串操作)
        char[] chars = CHAR_BUF_CACHE.get();
        message.getChars(0, Math.min(message.length(), chars.length), chars, 0);
        // è§£æžæ•°æ®é•¿åº¦ï¼ˆç›´æŽ¥è®¡ç®—)
        int dataLength = (fastHexToByte(chars[6], chars[7]) * 2) + 8;
        if (message.length() != dataLength) {
            return null; // é•¿åº¦ä¸åŒ¹é…
        }
        // è§£æžæ ‡ç­¾ä¿¡æ¯ï¼ˆæ— ä¸´æ—¶å¯¹è±¡åˆ›å»ºï¼‰
        parseTagInfo(chars, result);
        // è§£æžåŸºç«™ä¿¡æ¯ï¼ˆæ•°ç»„复用)
        parseAnchorInfo(chars, result);
        return result;
    }
    /**
     * æ ‡ç­¾ä¿¡æ¯è§£æžï¼ˆæ— å¯¹è±¡åˆ›å»ºï¼‰
     */
    private static void parseTagInfo(char[] chars, ParseResult result) {
        // æ ‡ç­¾ID(直接计算)
        result.tagId = (fastHexToByte(chars[10], chars[11]) << 8) |
                      fastHexToByte(chars[8], chars[9]);
        // åŒ…序列号(直接计算)
        result.sequenceNum = (fastHexToByte(chars[14], chars[15]) << 8) |
                            fastHexToByte(chars[12], chars[13]);
        // ç”µé‡ï¼ˆå•字节转换)
        result.power = fastHexToByte(chars[16], chars[17]);
        // æŒ‰é”®çŠ¶æ€ï¼ˆä½è¿ç®—ä»£æ›¿å­—ç¬¦ä¸²æ“ä½œï¼‰
        int buttonState = fastHexToByte(chars[18], chars[19]);
        result.vibrationState = (buttonState >> 5) & 1; // ç¬¬2位
        result.tagRemoved = ((buttonState >> 3) & 1) == 1; // ç¬¬4位
        result.isSleeping = ((buttonState >> 2) & 1) == 1; // ç¬¬5位
        result.isStatic = ((buttonState >> 1) & 1) == 1; // ç¬¬6位
        result.sosButtonPressed = (buttonState & 1) == 1; // ç¬¬7位
        // æ ‡ç­¾é«˜åº¦ï¼ˆç›´æŽ¥è®¡ç®—)
        result.tagHeight = (fastHexToByte(chars[22], chars[23]) << 8) |
                          fastHexToByte(chars[20], chars[21]);
    }
    /**
     * åŸºç«™ä¿¡æ¯è§£æžï¼ˆæ•°ç»„复用)
     */
    private static void parseAnchorInfo(char[] chars, ParseResult result) {
        // åŸºç«™æ•°é‡
        result.anchorCount = fastHexToByte(chars[32], chars[33]);
        if (result.anchorCount == 0) return;
        // æ•°ç»„复用检查(避免重复创建)
        if (result.anchorIds.length < result.anchorCount) {
            result.anchorIds = new int[result.anchorCount];
            result.distances = new int[result.anchorCount];
            result.anchorPowers = new int[result.anchorCount];
            result.signalStrengths1 = new int[result.anchorCount];
            result.signalStrengths2 = new int[result.anchorCount];
        }
        // åŸºç«™ä¿¡æ¯è§£æžï¼ˆç›´æŽ¥ç´¢å¼•计算)
        int baseIndex = 34;
        int powerIndex = 34 + result.anchorCount * 4;
        for (int i = 0; i < result.anchorCount; i++) {
            // åŸºç«™ID(小端序)
            int idLow = fastHexToByte(chars[baseIndex], chars[baseIndex+1]);
            int idHigh = fastHexToByte(chars[baseIndex+2], chars[baseIndex+3]);
            result.anchorIds[i] = (idHigh << 8) | idLow;
            // åŸºç«™è·ç¦»
            int distLow = fastHexToByte(chars[baseIndex+4], chars[baseIndex+5]);
            int distHigh = fastHexToByte(chars[baseIndex+6], chars[baseIndex+7]);
            result.distances[i] = (distHigh << 8) | distLow;
            // åŸºç«™ç”µé‡
            result.anchorPowers[i] = fastHexToByte(chars[powerIndex], chars[powerIndex+1]);
            baseIndex += 8; // æ¯ä¸ªåŸºç«™å ç”¨8字符
            powerIndex += 2; // æ¯ä¸ªç”µé‡å ç”¨2字符
        }
    }
    /**
     * å¿«é€Ÿåå…­è¿›åˆ¶è½¬å­—节(查表法)
     */
    private static int fastHexToByte(char c1, char c2) {
        int high = (c1 < 128) ? HEX_VALUES[c1] : -1;
        int low = (c2 < 128) ? HEX_VALUES[c2] : -1;
        if (high < 0 || low < 0) return 0; // æ— æ•ˆå­—符处理
        return (high << 4) | low;
    }
}
src/dell_system/SerialCommPanel.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,586 @@
package dell_system;
import com.fazecast.jSerialComm.SerialPort;
import Dell55aa.Dell55AA12HighPerf;
import Dell55aa.Dell55AA12HighPerf.ParseResult;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ResourceBundle;
public class SerialCommPanel extends JPanel {
    private static final long serialVersionUID = 1L;
    private final ResourceBundle messages;
    private final SerialPortService serialService = new SerialPortService();
    // æ–°å¢žå­—段
    private int packetCounter = 0;
    private JLabel lblParseStatus;
    private JCheckBox chkEnableParsing;
    private JLabel lblPacketCount; // æ–°å¢žï¼šæ•°æ®åŒ…计数标签
    // UI ç»„ä»¶
    private JComboBox<String> cbPorts;
    private JComboBox<String> cbBaudRate;
    private JCheckBox chkHexDisplay;
    private JCheckBox chkTimestamp;
    private JCheckBox chkAppendNewline;
    private JCheckBox chkHexSend;
    private JCheckBox chkTimedSend;
    private JTextField txtInterval;
    private JButton btnOpenSerial;
    private JButton btnStart;
    private JButton btnPause;
    private JButton btnClear;
    private JTextField txtSendData;
    private JButton btnSend;
    private JTextArea txtRawData;
    private JTextArea txtParsedData;
    private JScrollPane rawScrollPane;
    private JScrollPane parsedScrollPane;
    private final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
    public SerialCommPanel(ResourceBundle messages) {
        this.messages = messages;
        initUI();
    }
    /* ================= UI åˆå§‹åŒ– ================= */
    private void initUI() {
        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;
        gbc.weightx = 0.3;
        JPanel portPanel = new JPanel(new BorderLayout(5, 0));
        portPanel.setBackground(new Color(240, 245, 249));
        JLabel lblPort = new JLabel(messages.getString("SERIAL_PORT"));
        lblPort.setFont(new Font("宋体", Font.BOLD, 12));
        portPanel.add(lblPort, BorderLayout.WEST);
        cbPorts = new JComboBox<>();
        cbPorts.setBackground(new Color(200, 255, 200));
        scanSerialPorts();
        cbPorts.addPopupMenuListener(new PopupMenuListener() {
            @Override
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                scanSerialPorts();
            }
            @Override
            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
            }
            @Override
            public void popupMenuCanceled(PopupMenuEvent e) {
            }
        });
        portPanel.add(cbPorts, BorderLayout.CENTER);
        add(portPanel, gbc);
        gbc.gridx++;
        JPanel baudPanel = new JPanel(new BorderLayout(5, 0));
        baudPanel.setBackground(new Color(240, 245, 249));
        JLabel lblBaud = new JLabel(messages.getString("BAUD_RATE"));
        lblBaud.setFont(new Font("宋体", Font.BOLD, 12));
        baudPanel.add(lblBaud, BorderLayout.WEST);
        cbBaudRate = new JComboBox<>(new String[]{
                "19200", "38400", "57600", "115200",
                "230400", "460800", "921600"
        });
        cbBaudRate.setBackground(new Color(255, 200, 255));
        cbBaudRate.setSelectedItem("115200");
        baudPanel.add(cbBaudRate, BorderLayout.CENTER);
        add(baudPanel, gbc);
        gbc.gridx++;
        btnOpenSerial = createStyledButton(messages.getString("OPEN_SERIAL"), new Color(70, 130, 180));
        btnOpenSerial.addActionListener(e -> toggleSerialPort());
        add(btnOpenSerial, gbc);
        /* ===== åŽŸå§‹æ•°æ®æ˜¾ç¤ºåŒºåŸŸ ===== */
        gbc.gridx = 0;
        gbc.gridy++;
        gbc.gridwidth = 3;
        gbc.weighty = 0.5;
        gbc.fill = GridBagConstraints.BOTH;
        txtRawData = new JTextArea();
        txtRawData.setFont(new Font("Monospaced", Font.PLAIN, 12));
        txtRawData.setEditable(false);
        txtRawData.setBackground(new Color(255, 200, 200));
        txtRawData.setLineWrap(true);
        txtRawData.setWrapStyleWord(false);
        rawScrollPane = new JScrollPane(txtRawData);
        rawScrollPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createLineBorder(Color.DARK_GRAY),
                messages.getString("RAW_DATA_WINDOW"),
                TitledBorder.LEFT, TitledBorder.TOP,
                new Font("宋体", Font.BOLD, 12), Color.BLACK));
        add(rawScrollPane, gbc);
        /* ===== è§£æžæ•°æ®æ˜¾ç¤ºåŒºåŸŸ ===== */
        gbc.gridy++;
        txtParsedData = new JTextArea();
        txtParsedData.setFont(new Font("Monospaced", Font.PLAIN, 12));
        txtParsedData.setEditable(false);
        txtParsedData.setBackground(new Color(255, 255, 200));
        txtParsedData.setLineWrap(true);
        txtParsedData.setWrapStyleWord(false);
        parsedScrollPane = new JScrollPane(txtParsedData);
        parsedScrollPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createLineBorder(Color.DARK_GRAY),
                messages.getString("PARSED_DATA_WINDOW"),
                TitledBorder.LEFT, TitledBorder.TOP,
                new Font("宋体", Font.BOLD, 12), Color.BLACK));
        add(parsedScrollPane, gbc);
        /* ===== ä¸ºè§£æžçª—口添加右键菜单 ===== */
        JPopupMenu parsedPopup = new JPopupMenu();
        JMenuItem clearItem = new JMenuItem(messages.getString("CLEAR"));
        JMenuItem exportItem = new JMenuItem(messages.getString("EXPORT"));
        clearItem.addActionListener(e -> txtParsedData.setText(""));
        exportItem.addActionListener(e -> exportParsedData());
        parsedPopup.add(clearItem);
        parsedPopup.add(exportItem);
        txtParsedData.setComponentPopupMenu(parsedPopup);
        /* ===== æŽ§åˆ¶å’Œæ˜¾ç¤ºé€‰é¡¹ ===== */
        gbc.gridy++;
        gbc.weighty = 0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        JPanel controlPanel = new JPanel(new BorderLayout(10, 0));
        controlPanel.setBackground(new Color(240, 245, 249));
        // å·¦ä¾§æŽ§åˆ¶æŒ‰é’®
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5));
        buttonPanel.setBackground(new Color(240, 245, 249));
        btnStart = createStyledButton(messages.getString("START"), new Color(70, 130, 180));
        btnPause = createStyledButton(messages.getString("PAUSE"), new Color(218, 165, 32));
        btnClear = createStyledButton(messages.getString("CLEAR"), new Color(205, 92, 92));
        btnStart.setEnabled(true);
        btnPause.setEnabled(false);
        btnStart.addActionListener(e -> startCapture());
        btnPause.addActionListener(e -> pauseCapture());
        btnClear.addActionListener(e -> clearDisplay());
        buttonPanel.add(btnStart);
        buttonPanel.add(btnPause);
        buttonPanel.add(btnClear);
        // æ–°å¢žï¼šæ•°æ®åŒ…计数标签
        lblPacketCount = new JLabel(String.format(messages.getString("packet.count.format"), 0));
        lblPacketCount.setFont(new Font("宋体", Font.BOLD, 12));
        lblPacketCount.setForeground(new Color(0, 100, 0)); // æ·±ç»¿è‰²
        buttonPanel.add(lblPacketCount);
        controlPanel.add(buttonPanel, BorderLayout.WEST);
        // å³ä¾§æ˜¾ç¤ºé€‰é¡¹
        JPanel checkboxPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5));
        checkboxPanel.setBackground(new Color(240, 245, 249));
        chkHexDisplay = createStyledCheckbox(messages.getString("HEX_DISPLAY"));
        chkTimestamp = createStyledCheckbox(messages.getString("TIMESTAMP"));
        chkAppendNewline = createStyledCheckbox(messages.getString("APPEND_NEWLINE"));
        chkHexSend = createStyledCheckbox(messages.getString("HEX_SEND"));
        chkTimedSend = createStyledCheckbox(messages.getString("TIMED_SEND"));
        chkHexDisplay.setSelected(true);
        chkTimestamp.setSelected(true);
        chkAppendNewline.setSelected(true);
        txtInterval = new JTextField("1000", 5);
        txtInterval.setEnabled(false);
        txtInterval.setFont(new Font("宋体", Font.PLAIN, 12));
        txtInterval.setBackground(Color.WHITE);
        chkTimedSend.addActionListener(e -> txtInterval.setEnabled(chkTimedSend.isSelected()));
        // æ–°å¢žè§£æžæŽ§åˆ¶é€‰é¡¹
        chkEnableParsing = createStyledCheckbox(messages.getString("ENABLE_PARSING"));
        chkEnableParsing.setSelected(true);
        // åˆ é™¤è§£æžå™¨ä¸‹æ‹‰æ¡†åŠå…¶æ ‡ç­¾
        checkboxPanel.add(chkHexDisplay);
        checkboxPanel.add(chkTimestamp);
        checkboxPanel.add(chkAppendNewline);
        checkboxPanel.add(chkHexSend);
        checkboxPanel.add(chkTimedSend);
        checkboxPanel.add(new JLabel(messages.getString("INTERVAL_MS")));
        checkboxPanel.add(txtInterval);
        checkboxPanel.add(chkEnableParsing);
        controlPanel.add(checkboxPanel, BorderLayout.EAST);
        // æ–°å¢žè§£æžçŠ¶æ€æŒ‡ç¤ºå™¨
        JPanel statusPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        statusPanel.setBackground(new Color(240, 245, 249));
        lblParseStatus = new JLabel(messages.getString("parser.ready"));
        lblParseStatus.setFont(new Font("宋体", Font.PLAIN, 12));
        lblParseStatus.setForeground(Color.BLUE);
        statusPanel.add(lblParseStatus);
        controlPanel.add(statusPanel, BorderLayout.CENTER);
        add(controlPanel, gbc);
        /* ===== å‘送区域 ===== */
        gbc.gridy++;
        JPanel sendPanel = new JPanel(new BorderLayout(10, 5));
        sendPanel.setBackground(new Color(240, 245, 249));
        txtSendData = new JTextField();
        txtSendData.setBackground(new Color(200, 200, 255));
        txtSendData.setFont(new Font("宋体", Font.PLAIN, 12));
        btnSend = createStyledButton(messages.getString("SEND"), new Color(60, 179, 113));
        btnSend.addActionListener(e -> sendData());
        sendPanel.add(new JLabel(messages.getString("SEND_DATA")), BorderLayout.WEST);
        sendPanel.add(txtSendData, BorderLayout.CENTER);
        sendPanel.add(btnSend, BorderLayout.EAST);
        add(sendPanel, gbc);
        addNotify();
    }
    /* ================= æ‰«æä¸²å£åˆ—表 ================= */
    private void scanSerialPorts() {
        cbPorts.removeAllItems();
        SerialPort[] ports = SerialPort.getCommPorts();
        if (ports.length == 0) {
            cbPorts.addItem("No port found");
            cbPorts.setEnabled(false);
            return;
        }
        for (SerialPort p : ports) {
            String item = p.getSystemPortName();
            String desc = p.getPortDescription();
            if (desc != null && !desc.trim().isEmpty()) {
                item += " (" + desc + ")";
            }
            cbPorts.addItem(item);
        }
        cbPorts.setEnabled(true);
    }
    /* ================= æ‰“å¼€/关闭串口 ================= */
    private void toggleSerialPort() {
        if (serialService.isOpen()) {
            serialService.close();
            btnOpenSerial.setText(messages.getString("OPEN_SERIAL"));
            btnStart.setEnabled(true);
            btnPause.setEnabled(false);
        } else {
            String selected = (String) cbPorts.getSelectedItem();
            if (selected == null || selected.startsWith("No port")) {
                JOptionPane.showMessageDialog(this,
                        messages.getString("SELECT_PORT_ERROR"),
                        messages.getString("ERROR"), JOptionPane.ERROR_MESSAGE);
                return;
            }
            String portName = selected.split(" ")[0];
            int baud = Integer.parseInt((String) cbBaudRate.getSelectedItem());
            if (serialService.open(portName, baud)) {
                btnOpenSerial.setText(messages.getString("CLOSE_SERIAL"));
                startCapture();
            } else {
                JOptionPane.showMessageDialog(this,
                        messages.getString("OPEN_PORT_ERROR"),
                        messages.getString("ERROR"), JOptionPane.ERROR_MESSAGE);
            }
        }
    }
    /* ================= æ•°æ®æŽ¥æ”¶æŽ§åˆ¶ ================= */
    private void startCapture() {
        if (!serialService.isOpen()) return;
        serialService.startCapture(this::appendRawData);
        btnStart.setEnabled(false);
        btnPause.setEnabled(true);
    }
    private void pauseCapture() {
        serialService.setPaused(!serialService.isPaused());
        btnPause.setText(serialService.isPaused()
                ? messages.getString("RESUME")
                : messages.getString("PAUSE"));
    }
    /* ================= è§£æžæ•°æ®æ˜¾ç¤º ================= */
    private void appendParsedData(byte[] data) {
        if (!chkEnableParsing.isSelected()) return;
        SwingUtilities.invokeLater(() -> {
            packetCounter++;
            lblPacketCount.setText(String.format(messages.getString("packet.count.format"), packetCounter)); // æ›´æ–°è®¡æ•°æ ‡ç­¾
            try {
                // å°†å­—节数据转换为十六进制字符串
                String hexData = bytesToHex(data).toUpperCase();
                // ä½¿ç”¨Dell55AA12HighPerf解析
                ParseResult result = Dell55AA12HighPerf.parse(hexData);
                if (result != null) {
                    // æž„建状态字符串
                    String buttonStatus = result.sosButtonPressed ? messages.getString("yes") : messages.getString("no");
                    String staticStatus = result.isStatic ? messages.getString("yes") : messages.getString("no");
                    String sleepStatus = result.isSleeping ? messages.getString("yes") : messages.getString("no");
                    String vibrationStatus = (result.vibrationState == 1) ? messages.getString("on") : messages.getString("off");
                    String uwbStatus = result.tagRemoved ? messages.getString("off") : messages.getString("on");
                    // æž„建基站信息
                    StringBuilder anchorIds = new StringBuilder();
                    StringBuilder distances = new StringBuilder();
                    StringBuilder anchorPowers = new StringBuilder();
                    for (int i = 0; i < result.anchorCount; i++) {
                        if (i > 0) {
                            anchorIds.append(",");
                            distances.append(",");
                            anchorPowers.append(",");
                        }
                        anchorIds.append(result.anchorIds[i]);
                        distances.append(result.distances[i]);
                        anchorPowers.append(result.anchorPowers[i]);
                    }
                    // æ ¼å¼åŒ–输出
                    StringBuilder parsedOutput = new StringBuilder();
                    // æ·»åŠ æ—¶é—´æˆ³ï¼ˆå¦‚æžœå‹¾é€‰ï¼‰
                    if (chkTimestamp.isSelected()) {
                        parsedOutput.append("[").append(sdf.format(new Date())).append("]\n");
                    }
                    parsedOutput.append(String.format(
                        "%s: %d\n%s: %d\n%s: %d%%\n%s: %s[%s], %s[%s], %s[%s], %s[%s], %s[%s]\n%s: %d\n%s: %d\n%s: (%s)\n%s: (%s)\n%s: (%s)\n\n",
                        messages.getString("label.id"), result.tagId,
                        messages.getString("label.sequence"), result.sequenceNum,
                        messages.getString("label.power"), result.power,
                        messages.getString("label.status"),
                        messages.getString("status.button"), buttonStatus,
                        messages.getString("status.static"), staticStatus,
                        messages.getString("status.sleeping"), sleepStatus,
                        messages.getString("status.vibration"), vibrationStatus,
                        messages.getString("status.uwb_switch"), uwbStatus,
                        messages.getString("label.tag_height"), result.tagHeight,
                        messages.getString("label.anchor_count"), result.anchorCount,
                        messages.getString("label.anchor_ids"), anchorIds.toString(),
                        messages.getString("label.distances"), distances.toString(),
                        messages.getString("label.anchor_powers"), anchorPowers.toString()
                    ));
                    txtParsedData.append(parsedOutput.toString());
                    txtParsedData.setCaretPosition(txtParsedData.getDocument().getLength());
                    lblParseStatus.setText(String.format("%s (Packets: %d)", messages.getString("parser.ready"), packetCounter));
                } else {
                    StringBuilder invalidMsg = new StringBuilder();
                    // æ·»åŠ æ—¶é—´æˆ³ï¼ˆå¦‚æžœå‹¾é€‰ï¼‰
                    if (chkTimestamp.isSelected()) {
                        invalidMsg.append("[").append(sdf.format(new Date())).append("]\n");
                    }
                    invalidMsg.append(String.format("[Packet #%d] Not a valid 55AA12 packet\n\n", packetCounter));
                    txtParsedData.append(invalidMsg.toString());
                    lblParseStatus.setText(String.format("Parser: Invalid (Packets: %d)", packetCounter));
                }
            } catch (Exception e) {
                StringBuilder errorMsg = new StringBuilder();
                // æ·»åŠ æ—¶é—´æˆ³ï¼ˆå¦‚æžœå‹¾é€‰ï¼‰
                if (chkTimestamp.isSelected()) {
                    errorMsg.append("[").append(sdf.format(new Date())).append("]\n");
                }
                errorMsg.append(String.format("[Packet #%d] Parse error: %s\n\n",
                        packetCounter, e.getMessage()));
                txtParsedData.append(errorMsg.toString());
                lblParseStatus.setText(String.format("Parser: Error (Packets: %d)", packetCounter));
            }
        });
    }
    /* ================= UI æ›´æ–° ================= */
    private void appendRawData(byte[] bytes) {
        SwingUtilities.invokeLater(() -> {
            StringBuilder sb = new StringBuilder();
            if (chkTimestamp.isSelected()) {
                sb.append("[").append(sdf.format(new Date())).append("] ");
            }
            if (chkHexDisplay.isSelected()) {
                for (byte b : bytes) {
                    sb.append(String.format("%02X ", b));
                }
            } else {
                for (byte b : bytes) {
                    if (b != 0) sb.append((char) b);
                }
            }
            sb.append("\n");
            txtRawData.append(sb.toString());
            txtRawData.setCaretPosition(txtRawData.getDocument().getLength());
            // è§¦å‘解析
            appendParsedData(bytes);
        });
    }
    private void sendData() {
        String data = txtSendData.getText().trim();
        if (data.isEmpty()) {
            JOptionPane.showMessageDialog(this,
                    messages.getString("EMPTY_SEND_DATA"),
                    messages.getString("WARNING"), JOptionPane.WARNING_MESSAGE);
            return;
        }
        byte[] sendBytes;
        if (chkHexSend.isSelected()) {
            try {
                sendBytes = hexStringToBytes(data);
                if (sendBytes == null) {
                    throw new IllegalArgumentException("HEX格式错误");
                }
            } catch (Exception ex) {
                JOptionPane.showMessageDialog(this,
                        "HEX格式错误,请输入有效的十六进制数据(如:01 0A FF)",
                        "格式错误", JOptionPane.ERROR_MESSAGE);
                return;
            }
        } else {
            sendBytes = data.getBytes();
            if (chkAppendNewline.isSelected()) {
                String newline = "\r\n";
                byte[] newlineBytes = newline.getBytes();
                byte[] combined = new byte[sendBytes.length + newlineBytes.length];
                System.arraycopy(sendBytes, 0, combined, 0, sendBytes.length);
                System.arraycopy(newlineBytes, 0, combined, sendBytes.length, newlineBytes.length);
                sendBytes = combined;
            }
        }
        if (!serialService.send(sendBytes)) {
            JOptionPane.showMessageDialog(this,
                    messages.getString("SEND_ERROR"),
                    messages.getString("ERROR"), JOptionPane.ERROR_MESSAGE);
        }
    }
    /* ================= å¯¼å‡ºè§£æžæ•°æ® ================= */
    private void exportParsedData() {
        JFileChooser fileChooser = new JFileChooser();
        fileChooser.setDialogTitle(messages.getString("EXPORT_PARSED_DATA"));
        if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
            try (PrintWriter writer = new PrintWriter(fileChooser.getSelectedFile())) {
                writer.write(txtParsedData.getText());
                JOptionPane.showMessageDialog(this,
                        messages.getString("EXPORT_SUCCESS"),
                        messages.getString("INFO"), JOptionPane.INFORMATION_MESSAGE);
            } catch (Exception ex) {
                JOptionPane.showMessageDialog(this,
                        messages.getString("EXPORT_FAILED") + ": " + ex.getMessage(),
                        messages.getString("ERROR"), JOptionPane.ERROR_MESSAGE);
            }
        }
    }
    /* ================= è¾…助方法 ================= */
    private byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.trim().isEmpty()) {
            return null;
        }
        hexString = hexString.replaceAll("\\s+", "");
        if (hexString.length() % 2 != 0) {
            return null;
        }
        try {
            byte[] result = new byte[hexString.length() / 2];
            for (int i = 0; i < result.length; i++) {
                int index = i * 2;
                String byteStr = hexString.substring(index, index + 2);
                result[i] = (byte) Integer.parseInt(byteStr, 16);
            }
            return result;
        } catch (NumberFormatException e) {
            return null;
        }
    }
    private JButton createStyledButton(String text, Color bg) {
        JButton b = new JButton(text);
        b.setFont(new Font("宋体", Font.BOLD, 12));
        b.setBackground(bg);
        b.setForeground(Color.WHITE);
        b.setFocusPainted(false);
        b.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(bg.darker(), 1),
                BorderFactory.createEmptyBorder(5, 15, 5, 15)));
        return b;
    }
    private JCheckBox createStyledCheckbox(String text) {
        JCheckBox c = new JCheckBox(text);
        c.setFont(new Font("宋体", Font.PLAIN, 12));
        c.setBackground(new Color(240, 245, 249));
        c.setForeground(new Color(70, 70, 70));
        return c;
    }
    /* ================= çª—口关闭处理 ================= */
    @Override
    public void addNotify() {
        super.addNotify();
        java.awt.Window w = SwingUtilities.getWindowAncestor(this);
        if (w != null) {
            w.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
                    serialService.close();
                }
            });
        }
    }
    // æ–°å¢žè¾…助方法:字节数组转十六进制字符串
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }
    // ä¿®æ”¹clearDisplay方法
    private void clearDisplay() {
        txtRawData.setText("");
        txtParsedData.setText("");
        packetCounter = 0;
        lblPacketCount.setText(String.format(messages.getString("packet.count.format"), 0));
        lblParseStatus.setText(messages.getString("parser.ready"));
    }
}
src/dell_system/SerialPortService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,165 @@
// å£°æ˜ŽåŒ…名 dell_system
package dell_system;
// å¯¼å…¥ä¸²å£é€šä¿¡åº“
import com.fazecast.jSerialComm.SerialPort;
// å¯¼å…¥ Java å‡½æ•°å¼æŽ¥å£ Consumer,用于数据接收回调
import java.util.function.Consumer;
// å¯¼å…¥å­—节数组输出流
import java.io.ByteArrayOutputStream;
/**
 * ä¸²å£æœåŠ¡ç±»
 * è´Ÿè´£ä¸²å£çš„æ‰“开、关闭、数据收发
 */
public class SerialPortService {
    // ä¸²å£å¯¹è±¡ï¼Œç”¨äºŽä¸Žç¡¬ä»¶é€šä¿¡
    private SerialPort port;
    // æ ‡è®°æ˜¯å¦æ­£åœ¨æ•获数据(线程运行中)
    private volatile boolean capturing = false;
    // æ ‡è®°æ˜¯å¦æš‚停接收数据
    private volatile boolean paused    = false;
    // æ•°æ®è¯»å–线程
    private Thread readerThread;
    /**
     * æ‰“开串口
     * @param portName ä¸²å£åç§°ï¼ˆå¦‚ COM3)
     * @param baud æ³¢ç‰¹çŽ‡ï¼ˆå¦‚ 9600)
     * @return æ˜¯å¦æˆåŠŸæ‰“å¼€
     */
    public boolean open(String portName, int baud) {
        // å¦‚果串口已打开,直接返回成功
        if (port != null && port.isOpen()) {
            return true;
        }
        // æ ¹æ®åç§°èŽ·å–ä¸²å£å®žä¾‹
        port = SerialPort.getCommPort(portName);
        // è®¾ç½®ä¸²å£å‚数:波特率、数据位8、停止位1、无校验位
        port.setComPortParameters(baud, 8, 1, SerialPort.NO_PARITY);
        // ä¿®æ”¹ä¸ºåŠé˜»å¡žæ¨¡å¼ï¼Œè¯»è¶…æ—¶1ms
        port.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 1, 0);
        // æ‰“开串口并返回结果
        return port.openPort();
    }
    /**
     * å…³é—­ä¸²å£
     */
    public void close() {
        // å…ˆåœæ­¢æ•°æ®æŽ¥æ”¶çº¿ç¨‹
        stopCapture();
        // å¦‚果串口已打开,关闭它
        if (port != null && port.isOpen()) {
            port.closePort();
        }
        // é‡Šæ”¾ä¸²å£å¼•用
        port = null;
    }
    /**
     * å¯åŠ¨æ•°æ®æŽ¥æ”¶çº¿ç¨‹
     * @param onReceived æ•°æ®æŽ¥æ”¶å›žè°ƒå‡½æ•°ï¼Œæ”¶åˆ°æ•°æ®æ—¶è°ƒç”¨
     */
    public void startCapture(Consumer<byte[]> onReceived) {
        if (capturing || port == null || !port.isOpen()) return;
        capturing = true;
        paused = false;
        readerThread = new Thread(() -> {
            // åˆ›å»ºæ•°æ®ç¼“冲区
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            // æœ€åŽæ”¶åˆ°æ•°æ®çš„æ—¶é—´æˆ³
            long lastReceivedTime = 0;
            // è¯»å–缓冲区
            byte[] readBuffer = new byte[200];
            while (capturing && port.isOpen()) {
                // å¤„理暂停状态
                if (paused) {
                    buffer.reset(); // æ¸…空累积数据
                    // æ¸…空串口接收缓冲区(读取并丢弃)
                    int len;
                    do {
                        len = port.readBytes(readBuffer, readBuffer.length);
                    } while (len > 0);
                    // é™ä½Žæš‚停状态下的CPU占用
                    try { Thread.sleep(100); } catch (InterruptedException ignore) {}
                    continue;
                }
                // 1. æ£€æŸ¥è¶…时并发送累积数据
                long currentTime = System.currentTimeMillis();
                if (buffer.size() > 0 && (currentTime - lastReceivedTime) >= 20) {
                    onReceived.accept(buffer.toByteArray());
                    buffer.reset();
                }
                // 2. è¯»å–新数据
                int len = port.readBytes(readBuffer, readBuffer.length);
                currentTime = System.currentTimeMillis(); // æ›´æ–°å½“前时间
                if (len > 0) {
                    // å†™å…¥ç¼“冲区并更新时间戳
                    buffer.write(readBuffer, 0, len);
                    lastReceivedTime = currentTime;
                }
                // 3. é™ä½ŽCPU占用:无数据且缓冲区空时短暂休眠
                if (len <= 0 && buffer.size() == 0) {
                    try { Thread.sleep(1); } catch (InterruptedException ignore) {}
                }
            }
            // çº¿ç¨‹ç»“束前发送剩余数据
            if (buffer.size() > 0) {
                onReceived.accept(buffer.toByteArray());
            }
        });
        readerThread.setDaemon(true);
        readerThread.start();
    }
    /**
     * åœæ­¢æ•°æ®æŽ¥æ”¶çº¿ç¨‹
     */
    public void stopCapture() {
        // è®¾ç½®æ•获标志为 false,通知线程退出
        capturing = false;
        // å¦‚果线程存在,等待最多500ms确保线程结束
        if (readerThread != null) {
            try { readerThread.join(500); } catch (InterruptedException ignore) {}
            // æ¸…空线程引用
            readerThread = null;
        }
    }
    // è®¾ç½®æš‚停状态
    public void setPaused(boolean paused) {
        this.paused = paused;
    }
    // èŽ·å–å½“å‰æ˜¯å¦æš‚åœ
    public boolean isPaused() {
        return paused;
    }
    // åˆ¤æ–­ä¸²å£æ˜¯å¦å·²æ‰“å¼€
    public boolean isOpen() {
        return port != null && port.isOpen();
    }
    /**
     * å‘送数据
     * @param data è¦å‘送的字节数组
     * @return æ˜¯å¦æˆåŠŸå‘é€
     */
    public boolean send(byte[] data) {
        // å¦‚果串口已初始化且已打开,发送数据并返回结果
        return port != null && port.isOpen() && port.writeBytes(data, data.length) > 0;
    }
}
src/publicsWay/JugeNumber.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
package publicsWay;
import java.util.regex.Pattern;
public final class JugeNumber { // æ·»åŠ final防止继承
    // ä½¿ç”¨final修饰正则常量
    private static final String DIGIT_REGEX = "\\d+"; // çº¯æ•°å­—正则
    private static final String CONTAIN_DIGIT_REGEX = ".*\\d.*"; // å«æ•°å­—正则
    private static final String LETTER_REGEX = "[a-zA-Z]+"; // çº¯å­—母
    private static final String CONTAIN_LETTER_REGEX = ".*[a-zA-Z].*"; // å«å­—母
    private static final String CHINESE_REGEX = "^[\u4e00-\u9fa5]+$"; // çº¯ä¸­æ–‡æ­£åˆ™
    private static final String LETTER_DIGIT_REGEX = "^[a-zA-Z0-9]+$"; // ä»…含字母数字
    private static final String CHINESE_LETTER_REGEX = "^[\u4e00-\u9fa5a-zA-Z]+$"; // ä¸­æ–‡+字母
    private static final String CHINESE_LETTER_DIGIT_REGEX = "^[\u4e00-\u9fa5a-zA-Z0-9]+$"; // ä¸­æ–‡+字母+数字
    // æ–¹æ³•名和逻辑
    public static boolean isValidInteger(String num, int maxLength) { // åŠŸèƒ½æ˜Žç¡®çš„æ–¹æ³•å
        return isInteger(num) && num.length() <= maxLength; // åˆå¹¶æ¡ä»¶åˆ¤æ–­
    }
    // é¢„编译正则提高性能
    private static final Pattern INTEGER_PATTERN = Pattern.compile("^[-+]?\\d+$"); // é¢„编译整数正则
    public static boolean isInteger(String str) {
        return str != null && INTEGER_PATTERN.matcher(str).matches(); // æ·»åŠ null检查
    }
    // ç®€åŒ–正则调用
    public static boolean isLetterDigit(String str) {
        return str != null && str.matches(LETTER_DIGIT_REGEX); // æ·»åŠ null检查
    }
    // ä¸­æ–‡æ£€æµ‹
    public static boolean isChinese(String con) {
        return con != null && con.matches(CHINESE_REGEX); // ä½¿ç”¨å®Œæ•´å­—符串匹配
    }
    // æ·»åŠ null检查
    public static boolean isLetterDigitOrChinese(String str) {
        return str != null && str.matches(CHINESE_LETTER_DIGIT_REGEX); // ç©ºå®‰å…¨æ£€æµ‹
    }
    // åˆå¹¶æ­£åˆ™æ£€æµ‹
    public static boolean checkChineseLetter(String str) {
        return str != null && str.matches(CHINESE_LETTER_REGEX); // ç›´æŽ¥ä½¿ç”¨æ­£åˆ™åŒ¹é…
    }
    // æ ‡ç‚¹æ£€æµ‹é€»è¾‘
    public static boolean checkPunctuation(String input) {
        if (input == null) return false; // ç©ºå€¼å¤„理
        return input.length() != input.replaceAll("\\p{P}", "").length(); // ç›´æŽ¥è¿”回比较结果
    }
    // ç®€åŒ–方法实现
    public static boolean isDigit(String str) {
        return str != null && str.matches(DIGIT_REGEX); // ç©ºå®‰å…¨æ£€æµ‹
    }
    public static boolean isLetter(String str) {
        return str != null && str.matches(LETTER_REGEX); // ç©ºå®‰å…¨æ£€æµ‹
    }
    public static boolean hasDigit(String str) {
        return str != null && str.matches(CONTAIN_DIGIT_REGEX); // ç©ºå®‰å…¨æ£€æµ‹
    }
    public static boolean hasLetter(String str) {
        return str != null && str.matches(CONTAIN_LETTER_REGEX); // ç©ºå®‰å…¨æ£€æµ‹
    }
}
src/publicsWay/UwbDataParser.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,140 @@
// UwbDataParser.java
package publicsWay;
import java.util.ArrayList;
import java.util.List;
public class UwbDataParser {
    private byte[] buffer = new byte[4096];
    private int bufferLength = 0;
 // æ·»åŠ æ–°çš„parse方法
    public String parse(byte[] data) {
        // æ·»åŠ æ•°æ®åˆ°ç¼“å†²åŒº
        appendData(data, data.length);
        // è§£æžæ•°æ®åŒ…
        List<DataPacket> packets = parsePackets();
        if (packets.isEmpty()) {
            return "No complete packet found in data";
        }
        StringBuilder result = new StringBuilder();
        for (DataPacket packet : packets) {
            result.append(packet.toString()).append("\n");
        }
        return result.toString();
    }
    public void appendData(byte[] newData, int length) {
        if (bufferLength + length > buffer.length) {
            byte[] newBuffer = new byte[bufferLength + length];
            System.arraycopy(buffer, 0, newBuffer, 0, bufferLength);
            buffer = newBuffer;
        }
        System.arraycopy(newData, 0, buffer, bufferLength, length);
        bufferLength += length;
    }
    public void clearBuffer() {
        bufferLength = 0;
    }
    public List<DataPacket> parsePackets() {
        List<DataPacket> packets = new ArrayList<>();
        int index = 0;
        while (index <= bufferLength - 4) {
            if (buffer[index] == 0x55 && (buffer[index + 1] & 0xFF) == 0xAA) {
                int dataType = buffer[index + 2] & 0xFF;
                int dataLenField = buffer[index + 3] & 0xFF;
                int totalPacketLen = 4 + dataLenField;
                if (index + totalPacketLen > bufferLength) {
                    break;
                }
                byte[] packetData = new byte[totalPacketLen];
                System.arraycopy(buffer, index, packetData, 0, totalPacketLen);
                if (verifyChecksum(packetData)) {
                    DataPacket packet = parseSinglePacket(packetData);
                    if (packet != null) {
                        packets.add(packet);
                    }
                    index += totalPacketLen;
                } else {
                    index++;
                }
            } else {
                index++;
            }
        }
        if (index > 0) {
            int remaining = bufferLength - index;
            System.arraycopy(buffer, index, buffer, 0, remaining);
            bufferLength = remaining;
        }
        return packets;
    }
    private boolean verifyChecksum(byte[] packet) {
        int len = packet.length;
        if (len < 4) return false;
        int sum = 0;
        for (int i = 2; i < len - 2; i++) {
            sum += packet[i] & 0xFF;
        }
        sum = ~sum & 0xFFFF;
        int receivedChecksum = ((packet[len - 1] & 0xFF) << 8) | (packet[len - 2] & 0xFF);
        return sum == receivedChecksum;
    }
    private DataPacket parseSinglePacket(byte[] packet) {
        int sequence = packet[4] & 0xFF;
        int tagId = ((packet[6] & 0xFF) << 8) | (packet[5] & 0xFF);
        int baseId = ((packet[8] & 0xFF) << 8) | (packet[7] & 0xFF);
        int distance = (packet[12] << 24) |
                     ((packet[11] & 0xFF) << 16) |
                     ((packet[10] & 0xFF) << 8) |
                     (packet[9] & 0xFF);
        int battery = packet[13] & 0xFF;
        int buttonStatus = packet[14] & 0xFF;
        return new DataPacket(sequence,
                            String.format("%04X", tagId),
                            String.format("%04X", baseId),
                            distance, battery, buttonStatus);
    }
    public static class DataPacket {
        private final int sequence;
        private final String tagId;
        private final String baseId;
        private final int distance;
        private final int battery;
        private final int buttonStatus;
        public DataPacket(int sequence, String tagId, String baseId,
                          int distance, int battery, int buttonStatus) {
            this.sequence = sequence;
            this.tagId = tagId;
            this.baseId = baseId;
            this.distance = distance;
            this.battery = battery;
            this.buttonStatus = buttonStatus;
        }
        @Override
        public String toString() {
            return String.format("包序: %d, æ ‡ç­¾: %s, åŸºç«™: %s, è·ç¦»: %d mm, ç”µé‡: %d%%, æŒ‰é”®: %d",
                                sequence, tagId, baseId, distance, battery, buttonStatus);
        }
    }
}
src/window/ContentPanelFactory.java
@@ -10,6 +10,7 @@
import dell_system.MapManagementPanel;
import dell_system.MessageViewPanel;
import dell_system.QuickCalculationPanel;
import dell_system.SerialCommPanel;
import dell_system.SystemSettingsPanel;
import dell_system.VersionInfoPanel;
import dell_targets.BatteryRecordPanel;
@@ -117,6 +118,8 @@
                panel.add(new MapManagementPanel(messages), BorderLayout.CENTER);
            } else if (nodeName.equals(messages.getString("Quick_Calc"))) {// å¿«æ·è®¡ç®—                
                panel.add(new QuickCalculationPanel(messages), BorderLayout.CENTER);
            } else if (nodeName.equals(messages.getString("Serial_Port_Tool"))) {// ä¸²å£å·¥å…·
                panel.add(new SerialCommPanel(messages), BorderLayout.CENTER);
            } else {
                // å¯¹äºŽæœªå®žçŽ°åŠŸèƒ½ï¼Œæ˜¾ç¤ºå¼€å‘ä¸­æç¤º
                JLabel label = new JLabel(messages.getString("DEVELOPING") + ": " + nodeName, SwingConstants.CENTER);
src/window/NavigationTreeFactory.java
@@ -72,6 +72,7 @@
        sysNode.add(new DefaultMutableTreeNode(messages.getString("DEPARTMENT_MANAGEMENT")));  // éƒ¨é—¨ç®¡ç†     
        sysNode.add(new DefaultMutableTreeNode(messages.getString("MAP_MANAGEMENT")));//地图管理      
        sysNode.add(new DefaultMutableTreeNode(messages.getString("Quick_Calc")));  //快捷计算
        sysNode.add(new DefaultMutableTreeNode(messages.getString("Serial_Port_Tool")));  //串口工具
        sysNode.add(new DefaultMutableTreeNode(messages.getString("VERSION_INFO")));          // ç‰ˆæœ¬ä¿¡æ¯
        root.add(sysNode);
        
systemfile/20250724log.txt
ÎļþÒÑɾ³ý
systemfile/20250726log.txt
ÎļþÒÑɾ³ý
systemfile/20250805log.txt
ÎļþÒÑɾ³ý
systemfile/20250806log.txt
ÎļþÒÑɾ³ý
systemfile/20250807log.txt
ÎļþÒÑɾ³ý
systemfile/Messages_en.properties
@@ -1,3 +1,47 @@
# New Resource Key-Value Pairs
packet.count.format=Packets Received: %d
label.id=Label ID
label.sequence=Packet Sequence
label.power=Battery Level
label.status=Status
label.tag_height=Tag Height
label.anchor_count=Anchor Count
label.anchor_ids=Anchor IDs
label.distances=Distance Values
label.anchor_powers=Anchor Battery Levels
status.button=Button
status.static=Static
status.sleeping=Sleeping
status.vibration=Vibration
status.uwb_switch=UWB Switch
parser.ready=Parser: Ready
yes=Yes
no=No
on=On
off=Off
HEX_SEND=Hex Send
Serial_Port_Tool=Serial Port Tool
SERIAL_PORT=Port
BAUD_RATE=Baud Rate
OPEN_SERIAL=Open Serial
CLOSE_SERIAL=Close Serial
HEX_DISPLAY=HEX Display
TIMESTAMP=Timestamp
APPEND_NEWLINE=Append Newline
TIMED_SEND=Timed Send
INTERVAL_MS=Interval (ms)
RAW_DATA_WINDOW=Raw Data Window
PARSED_DATA_WINDOW=Parsed Data Window
START=Start
PAUSE=Pause
RESUME=Resume
CLEAR=Clear
SEND_DATA=Send Data
SEND=Send
SELECT_PORT_ERROR=Please select a serial port
OPEN_PORT_ERROR=Error opening port
EMPTY_SEND_DATA=Send data is empty
INVALID_INTERVAL=Invalid interval value
# New resource key\u2013value pairs  
map.dialog.selectMap=Select  
map.dialog.selectMapImage=Select Map Image  
systemfile/Messages_zh.properties
@@ -1,4 +1,62 @@
# æ–°å¢žèµ„源键值对
packet.count.format=收到数据:%d条
label.id=标签编号
label.sequence=包序
label.power=电量
label.status=状态
label.tag_height=标签高
label.anchor_count=基站数
label.anchor_ids=基站编号
label.distances=测距值
label.anchor_powers=基站电量
status.button=按键
status.static=静止
status.sleeping=休眠
status.vibration=震动
status.uwb_switch=UWB开关
parser.ready=解析器: å°±ç»ª
yes=是
no=否
on=开
off=关
ENABLE_PARSING=Enable Parsing
PARSER=Parser
EXPORT=Export
EXPORT_PARSED_DATA=Export Parsed Data
EXPORT_SUCCESS=Data exported successfully
EXPORT_FAILED=Export failed
INFO=Information
ENABLE_PARSING=启用解析
PARSER=解析器
EXPORT=导出
EXPORT_PARSED_DATA=导出已解析的数据
EXPORT_SUCCESS=数据导出成功
EXPORT_FAILED=导出失败
INFO=信息
HEX_SEND=Hex发送
Serial_Port_Tool=串口工具
SERIAL_PORT=串口
BAUD_RATE=选择波特率
OPEN_SERIAL=打开串口
CLOSE_SERIAL=关闭串口
HEX_DISPLAY=十六进制显示
TIMESTAMP=带时间戳显示
APPEND_NEWLINE=附加换行符
TIMED_SEND=定时发送
INTERVAL_MS=间隔(毫秒)
RAW_DATA_WINDOW=原始数据窗口
PARSED_DATA_WINDOW=解析数据窗口
START=开始
PAUSE=暂停
RESUME=恢复
CLEAR=清空
SEND_DATA=发送数据
SEND=发送
SELECT_PORT_ERROR=请选择串口
OPEN_PORT_ERROR=打开串口出错
EMPTY_SEND_DATA=发送数据为空
INVALID_INTERVAL=无效的间隔值
# æ–°å¢žèµ„源键值对
map.dialog.selectMap=选择
map.dialog.selectMapImage=选择地图图片
map.dialog.imageFiles=图片文件 (*.jpg, *.png, *.jpeg)
systemfile/logfile/openlog.txt
@@ -435,3 +435,123 @@
程序关闭: 2025-08-07 09:27:57
工作时长: 0小时 0分钟 10秒
-----------------------------------
程序启动: 2025-08-07 15:26:36
程序关闭: 2025-08-07 15:49:00
工作时长: 0小时 22分钟 24秒
-----------------------------------
程序启动: 2025-08-07 15:49:02
程序关闭: 2025-08-07 16:07:45
工作时长: 0小时 18分钟 42秒
-----------------------------------
程序启动: 2025-08-07 16:07:47
程序关闭: 2025-08-07 16:22:13
工作时长: 0小时 14分钟 26秒
-----------------------------------
程序启动: 2025-08-07 16:22:15
程序关闭: 2025-08-07 16:23:34
工作时长: 0小时 1分钟 18秒
-----------------------------------
程序启动: 2025-08-07 16:23:40
程序关闭: 2025-08-07 16:31:55
工作时长: 0小时 8分钟 15秒
-----------------------------------
程序启动: 2025-08-07 16:31:58
程序关闭: 2025-08-07 16:42:32
工作时长: 0小时 10分钟 34秒
-----------------------------------
程序启动: 2025-08-07 16:42:34
程序关闭: 2025-08-07 16:54:32
工作时长: 0小时 11分钟 58秒
-----------------------------------
程序启动: 2025-08-07 16:54:35
程序关闭: 2025-08-07 16:54:46
工作时长: 0小时 0分钟 10秒
-----------------------------------
程序启动: 2025-08-07 16:55:47
程序关闭: 2025-08-07 17:03:48
工作时长: 0小时 8分钟 1秒
-----------------------------------
程序启动: 2025-08-07 17:03:50
程序关闭: 2025-08-07 17:04:35
工作时长: 0小时 0分钟 44秒
-----------------------------------
程序启动: 2025-08-07 17:04:38
程序关闭: 2025-08-07 17:06:23
工作时长: 0小时 1分钟 45秒
-----------------------------------
程序启动: 2025-08-07 17:06:26
程序关闭: 2025-08-07 17:08:05
工作时长: 0小时 1分钟 39秒
-----------------------------------
程序启动: 2025-08-07 17:08:34
程序关闭: 2025-08-07 17:09:07
工作时长: 0小时 0分钟 32秒
-----------------------------------
程序启动: 2025-08-07 17:13:45
程序关闭: 2025-08-07 17:26:41
工作时长: 0小时 12分钟 55秒
-----------------------------------
程序启动: 2025-08-07 17:26:43
程序关闭: 2025-08-07 17:49:13
工作时长: 0小时 22分钟 29秒
-----------------------------------
程序启动: 2025-08-07 17:49:41
程序关闭: 2025-08-07 18:16:31
工作时长: 0小时 26分钟 50秒
-----------------------------------
程序启动: 2025-08-07 18:24:33
程序关闭: 2025-08-07 18:28:24
工作时长: 0小时 3分钟 50秒
-----------------------------------
程序启动: 2025-08-07 18:40:06
程序关闭: 2025-08-07 18:40:20
工作时长: 0小时 0分钟 13秒
-----------------------------------
程序启动: 2025-08-07 18:52:37
程序关闭: 2025-08-07 18:54:05
工作时长: 0小时 1分钟 28秒
-----------------------------------
程序启动: 2025-08-07 18:54:06
程序关闭: 2025-08-07 18:54:36
工作时长: 0小时 0分钟 29秒
-----------------------------------
程序启动: 2025-08-07 18:54:43
程序关闭: 2025-08-07 18:55:44
工作时长: 0小时 1分钟 0秒
-----------------------------------
程序启动: 2025-08-07 19:08:21
程序关闭: 2025-08-07 19:11:38
工作时长: 0小时 3分钟 17秒
-----------------------------------
程序启动: 2025-08-07 22:44:32
程序关闭: 2025-08-07 23:07:41
工作时长: 0小时 23分钟 9秒
-----------------------------------
程序启动: 2025-08-07 23:07:43
程序关闭: 2025-08-07 23:39:06
工作时长: 0小时 31分钟 23秒
-----------------------------------
程序启动: 2025-08-07 23:39:08
程序关闭: 2025-08-07 23:39:49
工作时长: 0小时 0分钟 41秒
-----------------------------------
程序启动: 2025-08-07 23:42:02
程序关闭: 2025-08-07 23:42:40
工作时长: 0小时 0分钟 37秒
-----------------------------------
程序启动: 2025-08-07 23:43:28
程序关闭: 2025-08-07 23:44:31
工作时长: 0小时 1分钟 3秒
-----------------------------------
程序启动: 2025-08-07 23:44:33
程序关闭: 2025-08-07 23:44:56
工作时长: 0小时 0分钟 22秒
-----------------------------------
程序启动: 2025-08-07 23:46:26
程序关闭: 2025-08-07 23:52:05
工作时长: 0小时 5分钟 39秒
-----------------------------------
程序启动: 2025-08-07 23:52:07
程序关闭: 2025-08-07 23:52:43
工作时长: 0小时 0分钟 36秒
-----------------------------------