lib/RXTXcomm.jarBinary files differ
lib/jSerialComm-2.10.4.jarBinary 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ç§ -----------------------------------