826220679@qq.com
2025-08-08 1562f05c0c85fb45cc8ffba8ac6984e9b6f96bd4
串口通信
已删除2个文件
已添加4个文件
已修改3个文件
785 ■■■■■ 文件已修改
src/Dell55aa/Dell55AA01.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Dell55aa/Dell55AA12HighPerf.java 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dell55AAData/Dell55AA01Parser.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dell55AAData/Dell55AA12HighPerf.java 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dell55AAData/HexUtils.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dell55AAData/PacketParser.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dell_system/SerialCommPanel.java 104 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/Messages_zh.properties 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
systemfile/logfile/openlog.txt 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Dell55aa/Dell55AA01.java
ÎļþÒÑɾ³ý
src/Dell55aa/Dell55AA12HighPerf.java
ÎļþÒÑɾ³ý
src/dell55AAData/Dell55AA01Parser.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
package dell55AAData;
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 =
        ThreadLocal.withInitial(ParseResult::new);
    // è§£æžç»“果类
    public static class ParseResult {
        public int sequenceNum;   // åºåˆ—号
        public String tagId;      // æ ‡ç­¾ID(4字节十六进制)
        public String anchorId;   // é”šç‚¹ID(4字节十六进制)
        public int distance;      // è·ç¦»(毫米)
        public int power;         // ç”µé‡(0-100)
        public boolean buttonPressed; // æŒ‰é’®çŠ¶æ€
        public void reset() {
            sequenceNum = 0;
            tagId = "";
            anchorId = "";
            distance = 0;
            power = 0;
            buttonPressed = false;
        }
    }
    /**
     * è§£æž55AA01协议数据
     * @param message åå…­è¿›åˆ¶å­—符串
     * @return è§£æžç»“æžœ(失败返回null)
     */
    public static ParseResult parse(String message) {
        // å‚数检查
        if (message == null || message.length() < MIN_LENGTH) {
            return null;
        }
        // åè®®å¤´éªŒè¯ (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;
        return result;
    }
}
src/dell55AAData/Dell55AA12HighPerf.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,168 @@
package dell55AAData;
public class Dell55AA12HighPerf {
    // åè®®å¸¸é‡
    @SuppressWarnings("unused")
    private static final String HEADER = "55AA12"; // åè®®å¤´
    private static final int MIN_LENGTH = 34; // æœ€å°æ•°æ®é•¿åº¦
    private static final ThreadLocal<ParseResult> RESULT_CACHE = // è§£æžç»“果缓存
        ThreadLocal.withInitial(ParseResult::new);
    // è§£æžç»“果类
    public static class ParseResult {
        public String tagId; // æ ‡ç­¾ID
        public int sequenceNum; // åºåˆ—号
        public int power; // ç”µé‡
        public int vibrationState; // æŒ¯åŠ¨çŠ¶æ€
        public boolean tagRemoved; // æ ‡ç­¾ç§»é™¤çŠ¶æ€
        public boolean isSleeping; // ä¼‘眠状态
        public boolean isStatic; // é™æ­¢çŠ¶æ€
        public boolean sosButtonPressed; // SOS按钮状态
        public int tagHeight; // æ ‡ç­¾é«˜åº¦
        public int anchorCount; // é”šç‚¹æ•°é‡
        public String[] anchorIds = new String[0]; // é”šç‚¹ID数组
        public int[] distances = new int[0]; // è·ç¦»æ•°ç»„
        public int[] anchorPowers = new int[0]; // é”šç‚¹ç”µé‡æ•°ç»„
        public int[] signalStrengths1 = new int[0]; // ä¿¡å·å¼ºåº¦1
        public int[] signalStrengths2 = new int[0]; // ä¿¡å·å¼ºåº¦2
        // é‡ç½®æ–¹æ³•
        public void reset() {
            tagId = "";
            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 = HexUtils.getThreadLocalBuffer();
        message.getChars(0, Math.min(message.length(), chars.length), chars, 0);
        // è§£æžæ•°æ®é•¿åº¦
        int dataLength = (HexUtils.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 = new String(new char[] {
                chars[10], chars[11], // é«˜å­—节
                chars[8], chars[9]    // ä½Žå­—节
            });
        // åºåˆ—号(小端序)
        result.sequenceNum = (HexUtils.fastHexToByte(chars[14], chars[15]) << 8 |
                            HexUtils.fastHexToByte(chars[12], chars[13]));
        // ç”µé‡
        result.power = HexUtils.fastHexToByte(chars[16], chars[17]);
        // çŠ¶æ€æ ‡å¿—
        int buttonState = HexUtils.fastHexToByte(chars[18], chars[19]);
        result.vibrationState = (buttonState >> 5) & 1;
        result.tagRemoved = ((buttonState >> 3) & 1) == 1;
        result.isSleeping = ((buttonState >> 2) & 1) == 1;
        result.isStatic = ((buttonState >> 1) & 1) == 1;
        result.sosButtonPressed = (buttonState & 1) == 1;
        // æ ‡ç­¾é«˜åº¦ï¼ˆå°ç«¯åºï¼‰
        result.tagHeight = (HexUtils.fastHexToByte(chars[22], chars[23]) << 8 |
                          HexUtils.fastHexToByte(chars[20], chars[21]));
    }
    /**
     * è§£æžé”šç‚¹ä¿¡æ¯
     */
    private static void parseAnchorInfo(char[] chars, ParseResult result) {
        // é”šç‚¹æ•°é‡
        result.anchorCount = HexUtils.fastHexToByte(chars[32], chars[33]);
        if (result.anchorCount == 0) return;
        // åŠ¨æ€æ‰©å±•æ•°ç»„
        if (result.anchorIds.length < result.anchorCount) {
            result.anchorIds = new String[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; // é”šç‚¹ID起始位置
        int distanceStart = baseIndex + result.anchorCount * 4; // è·ç¦»èµ·å§‹ä½ç½®
        int powerStart = distanceStart + result.anchorCount * 4; // ç”µé‡èµ·å§‹ä½ç½®
        // è§£æžé”šç‚¹ID(小端序)
        for (int i = 0; i < result.anchorCount; i++) {
            int idOffset = baseIndex + i * 4;
            result.anchorIds[i] = new String(new char[]{
                chars[idOffset + 2], // é«˜å­—节1
                chars[idOffset + 3], // é«˜å­—节2
                chars[idOffset],     // ä½Žå­—节1
                chars[idOffset + 1]  // ä½Žå­—节2
            });
        }
        // è§£æžè·ç¦»ï¼ˆæœ‰ç¬¦å·æ•´æ•°å¤„理)
        for (int i = 0; i < result.anchorCount; i++) {
            int distOffset = distanceStart + i * 4;
            int distLow = HexUtils.fastHexToByte(chars[distOffset], chars[distOffset + 1]);
            int distHigh = HexUtils.fastHexToByte(chars[distOffset + 2], chars[distOffset + 3]);
            int rawDistance = (distHigh << 8) | distLow;
            result.distances[i] = (rawDistance > 0x7FFF)
                                ? (rawDistance - 0x10000)
                                : rawDistance;
        }
        // è§£æžé”šç‚¹ç”µé‡
        for (int i = 0; i < result.anchorCount; i++) {
            int powerOffset = powerStart + i * 2;
            result.anchorPowers[i] = HexUtils.fastHexToByte(chars[powerOffset], chars[powerOffset + 1]);
        }
    }
}
src/dell55AAData/HexUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package dell55AAData;
public class HexUtils {
    // åå…­è¿›åˆ¶å­—符快速转换表 (ASCII范围内)
    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;
        }
    }
    // çº¿ç¨‹å®‰å…¨çš„字符缓冲区 (初始大小256)
    private static final ThreadLocal<char[]> CHAR_BUF_CACHE =
        ThreadLocal.withInitial(() -> new char[256]);
    /**
     * èŽ·å–çº¿ç¨‹æœ¬åœ°å­—ç¬¦ç¼“å†²åŒº
     * @return å¯å¤ç”¨çš„char[256]缓冲区
     */
    public static char[] getThreadLocalBuffer() {
        return CHAR_BUF_CACHE.get();
    }
    /**
     * å¿«é€Ÿå°†ä¸¤ä¸ªåå…­è¿›åˆ¶å­—符转换为字节
     * @param c1 é«˜ä½å­—符 (0-9, A-F, a-f)
     * @param c2 ä½Žä½å­—符 (0-9, A-F, a-f)
     * @return è½¬æ¢åŽçš„字节值 (无效字符返回0)
     */
    public 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/dell55AAData/PacketParser.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
package dell55AAData;
import dell55AAData.Dell55AA12HighPerf;
import dell55AAData.Dell55AA12HighPerf.ParseResult;
import dell55AAData.Dell55AA01Parser;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ResourceBundle;
/**
 * å…¬å…±è§£æžå™¨ï¼šæ ¹æ®åŒ…头统一解析 55AA01 / 55AA12 / æœªçŸ¥åè®®
 * å¯è¢«ä»»ä½• UI æˆ–业务类直接调用
 */
public final class PacketParser {
    private static final SimpleDateFormat SDF = new SimpleDateFormat("HH:mm:ss.SSS");
    private PacketParser() { /* å·¥å…·ç±»ï¼Œç¦æ­¢å®žä¾‹åŒ– */ }
    /**
     * è§£æžå…¥å£
     *
     * @param data     å®Œæ•´å­—节数组
     * @param bundle   å›½é™…化资源
     * @param showTime æ˜¯å¦åœ¨ç»“果前加时间戳
     * @return è§£æžåŽçš„可读字符串,若解析失败返回提示
     */
    public static String parse(byte[] data,ResourceBundle bundle,boolean showTime) {
        String hex = bytesToHex(data).toUpperCase();
        StringBuilder sb = new StringBuilder();
        if (showTime) {
            sb.append('[').append(SDF.format(new Date())).append("]");
        }
        if (hex.startsWith("55AA01")) {
            Dell55AA01Parser.ParseResult r = Dell55AA01Parser.parse(hex);
            if (r == null) {
                sb.append(bundle.getString("parser.invalid")).append(" 55AA01");
                return sb.toString();
            }
            String button = r.buttonPressed ? bundle.getString("yes") : bundle.getString("no");
            sb.append(bundle.getString("label.sequence")).append(": ").append(r.sequenceNum).append(',')
              .append(bundle.getString("label.id")).append(": ").append(r.tagId).append(',')
              .append(bundle.getString("label.anchor_ids")).append(": ").append(r.anchorId).append(',')
              .append(bundle.getString("label.distances")).append(": ").append(r.distance).append(',')
              .append(bundle.getString("label.power")).append(": ").append(r.power).append("%,")
              .append(bundle.getString("status.button")).append(": ").append(button);
        } else if (hex.startsWith("55AA12")) {
            ParseResult r = Dell55AA12HighPerf.parse(hex);
            if (r == null) {
                sb.append(bundle.getString("parser.invalid")).append(" 55AA12");
                return sb.toString();
            }
            // ç»„装基站信息
            StringBuilder ids = new StringBuilder();
            StringBuilder dists = new StringBuilder();
            StringBuilder powers = new StringBuilder();
            for (int i = 0; i < r.anchorCount; i++) {
                if (i > 0) {
                    ids.append(',');
                    dists.append(',');
                    powers.append(',');
                }
                ids.append(r.anchorIds[i]);
                dists.append(r.distances[i]);
                powers.append(r.anchorPowers[i]);
            }
            String button   = r.sosButtonPressed ? bundle.getString("yes") : bundle.getString("no");
            String stat     = r.isStatic        ? bundle.getString("yes") : bundle.getString("no");
            String sleep    = r.isSleeping      ? bundle.getString("yes") : bundle.getString("no");
            String vibrate  = (r.vibrationState == 1) ? bundle.getString("on") : bundle.getString("off");
            String uwb      = r.tagRemoved      ? bundle.getString("off") : bundle.getString("on");
            sb.append(bundle.getString("label.id")).append(": ").append(r.tagId).append('\n')
              .append(bundle.getString("label.sequence")).append(": ").append(r.sequenceNum).append('\n')
              .append(bundle.getString("label.power")).append(": ").append(r.power).append("%\n")
              .append(bundle.getString("label.status")).append(": ")
              .append(bundle.getString("status.button")).append('[').append(button).append("], ")
              .append(bundle.getString("status.static")).append('[').append(stat).append("], ")
              .append(bundle.getString("status.sleeping")).append('[').append(sleep).append("], ")
              .append(bundle.getString("status.vibration")).append('[').append(vibrate).append("], ")
              .append(bundle.getString("status.uwb_switch")).append('[').append(uwb).append("]\n")
              .append(bundle.getString("label.tag_height")).append(": ").append(r.tagHeight).append('\n')
              .append(bundle.getString("label.anchor_count")).append(": ").append(r.anchorCount).append('\n')
              .append(bundle.getString("label.anchor_ids")).append(": (").append(ids).append(")\n")
              .append(bundle.getString("label.distances")).append(": (").append(dists).append(")\n")
              .append(bundle.getString("label.anchor_powers")).append(": (").append(powers).append(")\n");
        } else {
            sb.append(bundle.getString("parser.unknown"));
        }
        sb.append("\n");
        return sb.toString();
    }
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) sb.append(String.format("%02X", b));
        return sb.toString();
    }
}
src/dell_system/SerialCommPanel.java
@@ -1,7 +1,6 @@
package dell_system;
import com.fazecast.jSerialComm.SerialPort;
import Dell55aa.Dell55AA12HighPerf;
import Dell55aa.Dell55AA12HighPerf.ParseResult;
import dell55AAData.PacketParser;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.PopupMenuEvent;
@@ -330,94 +329,24 @@
        
        SwingUtilities.invokeLater(() -> {
            packetCounter++;
            lblPacketCount.setText(String.format(messages.getString("packet.count.format"), 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());
                String parsed = PacketParser.parse(data, messages, chkTimestamp.isSelected());
                txtParsedData.append(parsed);
                    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("%s (Packets: %d)",
                                      messages.getString("parser.ready"), packetCounter));
            } catch (Exception ex) {
                StringBuilder sb = new StringBuilder();
                if (chkTimestamp.isSelected()) sb.append('[').append(sdf.format(new Date())).append("]\n");
                sb.append(String.format("[Packet #%d] Parse error: %s\n\n", packetCounter, ex.getMessage()));
                txtParsedData.append(sb.toString());
                lblParseStatus.setText(String.format("Parser: Error (Packets: %d)", packetCounter));
            }
        });
    }
    /* ================= UI æ›´æ–° ================= */
    private void appendRawData(byte[] bytes) {
        SwingUtilities.invokeLater(() -> {
@@ -564,15 +493,6 @@
                }
            });
        }
    }
    // æ–°å¢žè¾…助方法:字节数组转十六进制字符串
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }
    
    // ä¿®æ”¹clearDisplay方法
systemfile/Messages_zh.properties
@@ -1,13 +1,13 @@
# æ–°å¢žèµ„源键值对
packet.count.format=收到数据:%d条
label.id=标签编号
label.sequence=包序
label.power=电量
label.status=状态
label.tag_height=标签高
label.anchor_count=基站数
label.sequence=通信包序
label.power=标签电量
label.status=标签状态
label.tag_height=标签高度
label.anchor_count=基站数量
label.anchor_ids=基站编号
label.distances=测距值
label.distances=测距距离
label.anchor_powers=基站电量
status.button=按键
status.static=静止
@@ -15,10 +15,10 @@
status.vibration=震动
status.uwb_switch=UWB开关
parser.ready=解析器: å°±ç»ª
yes=是
no=否
on=开
off=关
yes=1
no=0
on=1
off=0
ENABLE_PARSING=Enable Parsing
PARSER=Parser
EXPORT=Export
systemfile/logfile/openlog.txt
@@ -555,3 +555,82 @@
程序关闭: 2025-08-07 23:52:43
工作时长: 0小时 0分钟 36秒
-----------------------------------
程序启动: 2025-08-08 11:29:39
程序启动: 2025-08-08 11:29:41
程序关闭: 2025-08-08 12:01:06
工作时长: 0小时 31分钟 26秒
-----------------------------------
程序启动: 2025-08-08 12:17:57
程序关闭: 2025-08-08 12:18:22
工作时长: 0小时 0分钟 25秒
-----------------------------------
程序启动: 2025-08-08 12:23:35
程序关闭: 2025-08-08 12:34:34
工作时长: 0小时 10分钟 59秒
-----------------------------------
程序启动: 2025-08-08 12:34:36
程序关闭: 2025-08-08 12:37:55
工作时长: 0小时 3分钟 18秒
-----------------------------------
程序启动: 2025-08-08 12:39:03
程序关闭: 2025-08-08 14:23:40
工作时长: 1小时 44分钟 37秒
-----------------------------------
程序启动: 2025-08-08 14:23:43
程序关闭: 2025-08-08 14:59:18
工作时长: 0小时 35分钟 35秒
-----------------------------------
程序启动: 2025-08-08 15:15:56
程序关闭: 2025-08-08 15:50:22
工作时长: 0小时 34分钟 26秒
-----------------------------------
程序启动: 2025-08-08 17:16:52
程序关闭: 2025-08-08 17:54:58
工作时长: 0小时 38分钟 5秒
-----------------------------------
程序启动: 2025-08-08 21:57:21
程序启动: 2025-08-08 22:21:20
程序关闭: 2025-08-08 22:21:27
工作时长: 0小时 24分钟 6秒
-----------------------------------
程序启动: 2025-08-08 22:21:29
程序关闭: 2025-08-08 22:22:07
工作时长: 0小时 0分钟 38秒
-----------------------------------
程序启动: 2025-08-08 22:25:06
程序关闭: 2025-08-08 22:25:45
工作时长: 0小时 0分钟 38秒
-----------------------------------
程序启动: 2025-08-08 22:26:08
程序关闭: 2025-08-08 22:26:57
工作时长: 0小时 0分钟 48秒
-----------------------------------
程序启动: 2025-08-08 22:27:44
程序关闭: 2025-08-08 22:28:32
工作时长: 0小时 0分钟 48秒
-----------------------------------
程序启动: 2025-08-08 22:29:10
程序关闭: 2025-08-08 22:29:41
工作时长: 0小时 0分钟 31秒
-----------------------------------
程序启动: 2025-08-08 22:30:36
程序关闭: 2025-08-08 22:31:01
工作时长: 0小时 0分钟 24秒
-----------------------------------
程序启动: 2025-08-08 22:31:22
程序启动: 2025-08-08 23:02:47
程序关闭: 2025-08-08 23:02:51
工作时长: 0小时 31分钟 29秒
-----------------------------------
程序启动: 2025-08-08 23:02:53
程序关闭: 2025-08-08 23:03:18
工作时长: 0小时 0分钟 25秒
-----------------------------------
程序启动: 2025-08-08 23:04:41
程序关闭: 2025-08-08 23:04:58
工作时长: 0小时 0分钟 16秒
-----------------------------------
程序启动: 2025-08-08 23:05:41
程序关闭: 2025-08-08 23:06:23
工作时长: 0小时 0分钟 41秒
-----------------------------------