.classpath
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,11 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"> <attributes> <attribute name="module" value="true"/> </attributes> </classpathentry> <classpathentry kind="lib" path="D:/eclipseworkspace/GIT/AOAFlow/lib/jSerialComm-2.10.4.jar"/> <classpathentry kind="output" path="bin"/> </classpath> .project
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,17 @@ <?xml version="1.0" encoding="UTF-8"?> <projectDescription> <name>AOAFlow</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> </natures> </projectDescription> bin/home/AOAFollowSystem$1.classBinary files differ
bin/home/AOAFollowSystem$HomePanel$1.classBinary files differ
bin/home/AOAFollowSystem$HomePanel$2.classBinary files differ
bin/home/AOAFollowSystem$HomePanel.classBinary files differ
bin/home/AOAFollowSystem.classBinary files differ
bin/home/ConfigPanel$1.classBinary files differ
bin/home/ConfigPanel.classBinary files differ
bin/home/Dell55AA01Parser$ParseResult.classBinary files differ
bin/home/Dell55AA01Parser.classBinary files differ
bin/home/Dell55AA1FParser$ParseResult.classBinary files differ
bin/home/Dell55AA1FParser.classBinary files differ
bin/home/FirmwareUpgrader$ProgressCallback.classBinary files differ
bin/home/FirmwareUpgrader.classBinary files differ
bin/home/HexUtils.classBinary files differ
bin/home/LogUtil.classBinary files differ
bin/home/Mains.classBinary files differ
bin/home/SerialPortService.classBinary files differ
bin/home/SingleInstanceLock.classBinary files differ
bin/home/VisualizationPanel$1.classBinary files differ
bin/home/VisualizationPanel.classBinary files differ
lib/jSerialComm-2.10.4.jarBinary files differ
src/home/AOAFollowSystem.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,775 @@ package home; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.TitledBorder; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import java.awt.*; import java.awt.event.*; import java.util.*; import java.io.InputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.io.File; import java.io.FileInputStream; import java.util.PropertyResourceBundle; import com.fazecast.jSerialComm.SerialPort; import home.Dell55AA01Parser.ParseResult; public class AOAFollowSystem extends JFrame { private static final long serialVersionUID = 1L; private CardLayout cardLayout; private JPanel mainPanel; private HomePanel homePanel; private ConfigPanel configPanel; private ResourceBundle messages; private Locale currentLocale; private JButton homeBtn; private JButton configBtn; private JComboBox<String> languageCombo; private JLabel languageLabel; private SerialPortService serialService; public AOAFollowSystem() { this.currentLocale = Locale.SIMPLIFIED_CHINESE; this.messages = loadResourceBundle(currentLocale); this.serialService = new SerialPortService(); initializeUI(); } private ResourceBundle loadResourceBundle(Locale locale) { String fileName = locale.equals(Locale.ENGLISH) ? "Messages_en.properties" : "Messages_zh.properties"; File langFile = new File("systemfile/" + fileName); if (!langFile.exists()) { System.err.println("é»è®¤èµæºæä»¶æªæ¾å°: " + langFile.getAbsolutePath()); return ResourceBundle.getBundle("systemfile.Messages"); } try (InputStream inputStream = new FileInputStream(langFile)) { return new PropertyResourceBundle(inputStream); } catch (IOException e) { System.err.println("æ æ³å è½½èµæºæä»¶: " + e.getMessage()); return ResourceBundle.getBundle("systemfile.Messages"); } } public String getString(String key) { if (messages != null && messages.containsKey(key)) { return messages.getString(key); } // é»è®¤ææ¬ï¼ä¸æï¼ switch (key) { case "home": return "é¦é¡µ"; case "config": return "é ç½®"; case "device_id": return "设å¤ç¼å·"; case "group": return "éä¿¡å°ç»"; case "frequency": return "éä¿¡é¢ç"; case "read_config": return "读åé ç½®"; case "save_config": return "ä¿åé ç½®"; case "serial_port": return "串å£"; case "baud_rate": return "æ³¢ç¹ç"; case "open_serial": return "æå¼ä¸²å£"; case "close_serial": return "å ³é串å£"; case "start": return "å¼å§"; case "pause": return "æå"; case "clear": return "æ¸ ç©º"; case "send": return "åé"; case "device_id_table": return "设å¤ç¼å·"; case "distance_table": return "宿¶è·ç¦»"; case "angle_table": return "宿¶è§åº¦"; case "signal_table": return "ä¿¡å·è´¨é"; case "power_table": return "çµé"; case "button_table": return "æé®"; case "time_table": return "æ¶é´"; case "LANGUAGE": return "è¯è¨"; case "log": return "æ¥å¿"; case "send_data": return "åéæ°æ®"; case "select_serial_port": return "è¯·éæ©ä¸²å£"; case "error": return "é误"; case "open_serial_first": return "è¯·å æå¼ä¸²å£"; case "input_data_to_send": return "请è¾å ¥è¦åéçæ°æ®"; case "send_failed": return "åé失败"; case "receive": return "æ¥æ¶"; case "visualization": return "å¯è§å"; case "config_read_success": return "é 置读åæå"; case "info": return "ä¿¡æ¯"; case "input_valid_number": return "请è¾å ¥ææçæ°å"; case "config_save_success": return "é ç½®ä¿åæå"; case "select_file": return "éæ©æä»¶"; case "upgrade": return "å级"; case "upgrade_progress": return "å级è¿åº¦"; case "select_bin_file": return "è¯·éæ©binæä»¶"; case "upgrade_success": return "å级æå"; case "upgrade_failed": return "å级失败"; case "hex": return "HEX"; case "ascii": return "ASCII"; case "hex_send": return "HEXåé"; case "display_format": return "æ¾ç¤ºæ ¼å¼"; default: return key; } } private void initializeUI() { setTitle("HXZK_AOA"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(1000, 700); setLocationRelativeTo(null); // å建顶é¨å¯¼èª JPanel navPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); homeBtn = createNavButton(getString("home"), new Color(70, 130, 180)); configBtn = createNavButton(getString("config"), new Color(70, 130, 180)); languageCombo = new JComboBox<>(new String[]{"䏿", "English"}); languageCombo.setSelectedIndex(0); languageCombo.addActionListener(e -> { Locale newLocale = languageCombo.getSelectedIndex() == 0 ? Locale.SIMPLIFIED_CHINESE : Locale.ENGLISH; switchLanguage(newLocale); }); navPanel.add(homeBtn); navPanel.add(configBtn); navPanel.add(Box.createHorizontalStrut(20)); languageLabel = new JLabel(getString("LANGUAGE") + ":"); navPanel.add(languageLabel); navPanel.add(languageCombo); // åå»ºä¸»é¢æ¿ï¼å¡çå¸å±ï¼ cardLayout = new CardLayout(); mainPanel = new JPanel(cardLayout); homePanel = new HomePanel(serialService, this); configPanel = new ConfigPanel(serialService, this); mainPanel.add(homePanel, "home"); mainPanel.add(configPanel, "config"); // æ·»å å¯¼èªæé®äºä»¶ homeBtn.addActionListener(e -> cardLayout.show(mainPanel, "home")); configBtn.addActionListener(e -> cardLayout.show(mainPanel, "config")); // 设置å¸å± setLayout(new BorderLayout()); add(navPanel, BorderLayout.NORTH); add(mainPanel, BorderLayout.CENTER); // 设置é»è®¤æ¾ç¤ºé¦é¡µ cardLayout.show(mainPanel, "home"); } private void switchLanguage(Locale newLocale) { this.currentLocale = newLocale; this.messages = loadResourceBundle(currentLocale); // æ´æ°çé¢ææ¬ updateUILanguage(); // æ´æ°è¯è¨éæ©æ¡ languageCombo.setSelectedIndex(newLocale.equals(Locale.SIMPLIFIED_CHINESE) ? 0 : 1); revalidate(); repaint(); } private void updateUILanguage() { // æ´æ°å¯¼èªæé®ææ¬ homeBtn.setText(getString("home")); configBtn.setText(getString("config")); // æ´æ°è¯è¨æ ç¾ languageLabel.setText(getString("LANGUAGE") + ":"); // æ´æ°å颿¿ææ¬ homePanel.updateLanguage(); configPanel.updateLanguage(); } private JButton createNavButton(String text, Color color) { JButton button = new JButton(text); button.setBackground(color); button.setForeground(Color.WHITE); button.setFocusPainted(false); button.setBorder(BorderFactory.createEmptyBorder(5, 15, 5, 15)); button.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { button.setBackground(color.darker()); } @Override public void mouseExited(MouseEvent e) { button.setBackground(color); } }); return button; } // é¦é¡µé¢æ¿ class HomePanel extends JPanel { /** * */ private static final long serialVersionUID = 1L; private VisualizationPanel visualizationPanel; private JTable dataTable; private JTextArea logArea; private JComboBox<String> serialPortCombo; private JComboBox<String> baudRateCombo; private JButton openSerialBtn; private JButton startPauseBtn; private JButton clearBtn; private JButton sendBtn; private JTextField sendField; private JLabel displayFormatLabel; // æ°å¢ï¼æ¾ç¤ºæ ¼å¼æ ç¾ private JLabel serialPortLabel; // æ°å¢ï¼ä¸²å£æ ç¾ private JLabel baudRateLabel; // æ°å¢ï¼æ³¢ç¹çæ ç¾ private SerialPortService serialService; private AOAFollowSystem parentFrame; private boolean serialOpened = false; private boolean isRunning = false; // æ°å¢ï¼æ¾ç¤ºæ ¼å¼åéæé® private JRadioButton hexRadio; private JRadioButton asciiRadio; private JCheckBox hexSendCheckBox; // è¡¨æ ¼æ¨¡å - åªæ1è¡ private Object[][] tableData = new Object[1][7]; private String[] columnNames = { getString("device_id_table"), getString("distance_table"), getString("angle_table"), getString("signal_table"), getString("power_table"), getString("button_table"), getString("time_table") }; // ä¼åï¼éç¨å¯¹è±¡åå°å ååé private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); private StringBuilder hexBuilder = new StringBuilder(); private StringBuilder displayBuilder = new StringBuilder(); public HomePanel(SerialPortService serialService, AOAFollowSystem parentFrame) { this.serialService = serialService; this.parentFrame = parentFrame; setLayout(new BorderLayout(10, 10)); setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // åå»ºå·¦ä¾§é¢æ¿ï¼å¯è§ååºååè¡¨æ ¼ï¼ JPanel leftPanel = new JPanel(new BorderLayout(10, 10)); // å¯è§å颿¿ visualizationPanel = new VisualizationPanel(parentFrame); leftPanel.add(visualizationPanel, BorderLayout.CENTER); // æ°æ®è¡¨æ ¼ JPanel tablePanel = new JPanel(new BorderLayout()); tablePanel.setBorder(BorderFactory.createTitledBorder(getString("device_id_table"))); dataTable = new JTable(tableData, columnNames); JScrollPane tableScroll = new JScrollPane(dataTable); tableScroll.setPreferredSize(new Dimension(0, 80)); tablePanel.add(tableScroll, BorderLayout.CENTER); leftPanel.add(tablePanel, BorderLayout.SOUTH); // å建å³ä¾§é¢æ¿ï¼æ¥å¿åæ§å¶ï¼ JPanel rightPanel = new JPanel(new BorderLayout(10, 10)); // æ¥å¿åºå JPanel logPanel = new JPanel(new BorderLayout()); logPanel.setBorder(BorderFactory.createTitledBorder(getString("log"))); logArea = new JTextArea(); logArea.setEditable(false); JScrollPane logScroll = new JScrollPane(logArea); logScroll.setPreferredSize(new Dimension(400, 0)); logPanel.add(logScroll, BorderLayout.CENTER); // æ°å¢ï¼æ¾ç¤ºæ ¼å¼éæ©é¢æ¿ JPanel formatPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); formatPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); displayFormatLabel = new JLabel(getString("display_format") + ":"); // 使ç¨getStringè·åææ¬ formatPanel.add(displayFormatLabel); ButtonGroup formatGroup = new ButtonGroup(); hexRadio = new JRadioButton(getString("hex"), true); // é»è®¤éä¸HEX asciiRadio = new JRadioButton(getString("ascii")); formatGroup.add(hexRadio); formatGroup.add(asciiRadio); formatPanel.add(hexRadio); formatPanel.add(asciiRadio); // å°æ ¼å¼éæ©é¢æ¿æ·»å å°æ¥å¿é¢æ¿çåºé¨ logPanel.add(formatPanel, BorderLayout.SOUTH); rightPanel.add(logPanel, BorderLayout.CENTER); // æ§å¶é¢æ¿ JPanel controlPanel = new JPanel(new GridBagLayout()); controlPanel.setBorder(BorderFactory.createTitledBorder(getString("serial_port"))); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(5, 2, 5, 5); gbc.fill = GridBagConstraints.HORIZONTAL; // 串å£éæ© gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 0; serialPortLabel = new JLabel(getString("serial_port") + ":"); // 使ç¨getStringè·åææ¬ controlPanel.add(serialPortLabel, gbc); gbc.gridx = 1; gbc.weightx = 1; serialPortCombo = new JComboBox<>(); // æ·»å å¼¹åºèåçå¬å¨ï¼ç¹å»æ¶å·æ°ä¸²å£å表 serialPortCombo.addPopupMenuListener(new PopupMenuListener() { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { refreshSerialPorts(); } @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {} @Override public void popupMenuCanceled(PopupMenuEvent e) {} }); refreshSerialPorts(); controlPanel.add(serialPortCombo, gbc); // æ³¢ç¹çéæ© gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 0; baudRateLabel = new JLabel(getString("baud_rate") + ":"); // 使ç¨getStringè·åææ¬ controlPanel.add(baudRateLabel, gbc); gbc.gridx = 1; gbc.weightx = 1; baudRateCombo = new JComboBox<>(new String[]{"115200", "921600","9600", "19200", "38400", "57600"}); baudRateCombo.setSelectedIndex(0); controlPanel.add(baudRateCombo, gbc); // 䏲壿§å¶æé® gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 1; // ä¿®æ¹ï¼åªå ä¸å gbc.weightx = 0.5; // ä¿®æ¹ï¼è®¾ç½®æé openSerialBtn = createColoredButton(getString("open_serial"), new Color(70, 130, 180)); openSerialBtn.setPreferredSize(new Dimension(100, 30)); // ä¿®æ¹ï¼è®¾ç½®ä¸å¼å§æé®ç¸å尺寸 openSerialBtn.addActionListener(e -> toggleSerialPort()); controlPanel.add(openSerialBtn, gbc); openSerialBtn.setBackground(Color.GRAY); // å¯å¨/æåæé®ï¼å并为ä¸ä¸ªï¼ gbc.gridx = 1; // ä¿®æ¹ï¼æ¾å¨ç¬¬äºå gbc.gridy = 2; JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0)); startPauseBtn = createColoredButton(getString("start"), new Color(50, 205, 50)); startPauseBtn.setPreferredSize(new Dimension(100, 30)); startPauseBtn.addActionListener(e -> toggleCapture()); buttonPanel.add(startPauseBtn); clearBtn = createColoredButton(getString("clear"), new Color(192, 192, 192)); clearBtn.setPreferredSize(new Dimension(100, 30)); clearBtn.addActionListener(e -> clearLog()); buttonPanel.add(clearBtn); controlPanel.add(buttonPanel, gbc); rightPanel.add(controlPanel, BorderLayout.SOUTH); // åéæ°æ®é¢æ¿ - ç§»å°è¡¨æ ¼ä¸æ¹ JPanel sendDataPanel = new JPanel(new BorderLayout(5, 5)); sendDataPanel.setBorder(BorderFactory.createTitledBorder(getString("send_data"))); JPanel sendControlPanel = new JPanel(new BorderLayout(5, 0)); sendField = new JTextField(); sendBtn = createColoredButton(getString("send"), new Color(70, 130, 180)); sendBtn.setPreferredSize(new Dimension(100, 30)); sendBtn.addActionListener(e -> sendData()); // æ°å¢ï¼HEXåéå¤éæ¡ hexSendCheckBox = new JCheckBox(getString("hex_send"), true); // é»è®¤å¾é sendControlPanel.add(hexSendCheckBox, BorderLayout.WEST); sendControlPanel.add(sendBtn, BorderLayout.EAST); sendDataPanel.add(sendField, BorderLayout.CENTER); sendDataPanel.add(sendControlPanel, BorderLayout.EAST); // æ·»å åéæ°æ®é¢æ¿å°å·¦ä¾§é¢æ¿çæåºé¨ leftPanel.add(sendDataPanel, BorderLayout.SOUTH); // æ·»å å·¦å³é¢æ¿ add(leftPanel, BorderLayout.CENTER); add(rightPanel, BorderLayout.EAST); } private JButton createColoredButton(String text, Color color) { JButton button = new JButton(text); button.setBackground(color); button.setForeground(Color.WHITE); button.setFocusPainted(false); button.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); button.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { button.setBackground(color.darker()); } @Override public void mouseExited(MouseEvent e) { button.setBackground(color); } }); return button; } private void refreshSerialPorts() { String selectedPort = (String) serialPortCombo.getSelectedItem(); serialPortCombo.removeAllItems(); SerialPort[] ports = SerialPort.getCommPorts(); for (SerialPort port : ports) { serialPortCombo.addItem(port.getSystemPortName()); } // å°è¯æ¢å¤ä¹åçéæ© if (selectedPort != null) { serialPortCombo.setSelectedItem(selectedPort); } else if (ports.length > 0) { serialPortCombo.setSelectedIndex(0); } } private void toggleSerialPort() { if (serialOpened) { closeSerialPort(); openSerialBtn.setText(getString("open_serial")); openSerialBtn.setBackground(Color.GRAY); // å ³éæ¶ç°è² serialOpened = false; // å ³é䏲壿¶åæ¢æè· if (isRunning) { toggleCapture(); } } else { if (openSerialPort()) { openSerialBtn.setText(getString("close_serial")); openSerialBtn.setBackground(Color.GREEN); serialOpened = true; } } } private boolean openSerialPort() { String portName = (String) serialPortCombo.getSelectedItem(); if (portName == null) { JOptionPane.showMessageDialog(this, getString("select_serial_port"), getString("error"), JOptionPane.ERROR_MESSAGE); return false; } int baudRate = Integer.parseInt((String) baudRateCombo.getSelectedItem()); return serialService.open(portName, baudRate); } private void closeSerialPort() { serialService.close(); } private void toggleCapture() { if (!serialOpened) { JOptionPane.showMessageDialog(this, getString("open_serial_first"), getString("error"), JOptionPane.ERROR_MESSAGE); return; } isRunning = !isRunning; serialService.setPaused(!isRunning); if (isRunning) { startPauseBtn.setText(getString("pause")); startPauseBtn.setBackground(new Color(255, 165, 0)); // å¯å¨æ°æ®æè· serialService.startCapture(this::processReceivedData); } else { startPauseBtn.setText(getString("start")); startPauseBtn.setBackground(new Color(50, 205, 50)); } } private void clearLog() { logArea.setText(""); } private void sendData() { if (!serialOpened) { JOptionPane.showMessageDialog(this, getString("open_serial_first"), getString("error"), JOptionPane.ERROR_MESSAGE); return; } String data = sendField.getText(); if (data.isEmpty()) { JOptionPane.showMessageDialog(this, getString("input_data_to_send"), getString("error"), JOptionPane.ERROR_MESSAGE); return; } byte[] sendBytes; if (hexSendCheckBox.isSelected()) { // HEXå鿍¡å¼ try { sendBytes = hexStringToByteArray(data.replaceAll("\\s+", "")); if (sendBytes == null) { JOptionPane.showMessageDialog(this, "æ æçHEXæ ¼å¼", getString("error"), JOptionPane.ERROR_MESSAGE); return; } } catch (Exception e) { JOptionPane.showMessageDialog(this, "HEX转æ¢é误: " + e.getMessage(), getString("error"), JOptionPane.ERROR_MESSAGE); return; } } else { // ASCIIå鿍¡å¼ sendBytes = data.getBytes(); } boolean success = serialService.send(sendBytes); if (success) { appendLog(getString("send") + ": " + (hexSendCheckBox.isSelected() ? bytesToHex(sendBytes) : data)); } else { appendLog(getString("send_failed") + ": " + data); } sendField.setText(""); } private void processReceivedData(byte[] data) { // ä¼åï¼éç¨StringBuilder对象 hexBuilder.setLength(0); for (byte b : data) { hexBuilder.append(String.format("%02X", b)); } String displayText = hexBuilder.toString(); String displayText1 = new String(data).replaceAll("\\s+", ""); if (displayText.startsWith("55AA1F")) { Dell55AA1FParser.ParseResult result = Dell55AA1FParser.parse(displayText, "127.0.0.1", 0); if (result != null) { updateTable(result); visualizationPanel.updatePosition(result.distance, result.angle); visualizationPanel.setTagId(result.tagId); } }else if(displayText.startsWith("55AA01")) { ParseResult result=Dell55AA01Parser.parse(displayText, "127.0.0.1", 0); visualizationPanel.updatePosition(result.distance,270); visualizationPanel.setTagId(result.tagId); } if (hexRadio.isSelected()) { appendLog(displayText); } else { if (displayText.startsWith("55AA1F")) { Dell55AA1FParser.ParseResult result = Dell55AA1FParser.parse(displayText, "127.0.0.1", 0); // ä¼åï¼éç¨StringBuilder对象 displayBuilder.setLength(0); displayBuilder.append("1F:") .append(result.dataLength) .append(",") .append(result.messageType) .append(",id:") .append(result.tagId) .append(",Dis:") .append(result.distance) .append("cm,Angle:") .append(result.angle) .append("°,Signal:") .append(result.signalQuality) .append(",Button:") .append(result.buttonPressed) .append(",Power:") .append(result.power); appendLog(displayBuilder.toString()); }else if(displayText.startsWith("55AA01")) { ParseResult result=Dell55AA01Parser.parse(displayText, "127.0.0.1", 0); // ä¼åï¼éç¨StringBuilder对象 displayBuilder.setLength(0); displayBuilder.append("55AA01 Seq:") .append(result.sequenceNum) .append(",Tagid:") .append(result.tagId) .append(",Anchorid:") .append(result.anchorId) .append(",Distance:") .append(result.distance) .append(",Power:") .append(result.power) .append(",Button:") .append(result.buttonPressed); appendLog(displayBuilder.toString()); }else { appendLog(displayText1); } } } private void appendLog(String message) { SwingUtilities.invokeLater(() -> { // ä¼åï¼éå¶æ¥å¿é¿åº¦ï¼é²æ¢å åæ éå¢é¿ if (logArea.getLineCount() > 1000) { try { int end = logArea.getDocument().getLength(); int start = logArea.getDocument().getText(0, end).indexOf("\n") + 1; logArea.getDocument().remove(0, start); } catch (Exception e) { logArea.setText(""); // 妿åºé忏 空æ¥å¿ } } String time = sdf.format(new Date()); logArea.append("[" + time + "] " + message + "\n"); // èªå¨æ»å¨å°æå logArea.setCaretPosition(logArea.getDocument().getLength()); }); } private void updateTable(Dell55AA1FParser.ParseResult result) { SwingUtilities.invokeLater(() -> { // åªæ1è¡æ°æ®ï¼ç´æ¥æ´æ° String time = sdf.format(new Date()); tableData[0] = new Object[]{ result.tagId, result.distance + "cm", result.angle + "°", result.signalQuality, result.power + "%", result.buttonPressed, time }; // å·æ°è¡¨æ ¼ dataTable.repaint(); }); } // è¾ å©æ¹æ³ï¼å°åèæ°ç»è½¬æ¢ä¸ºHEXå符串 private String bytesToHex(byte[] bytes) { // ä¼åï¼éç¨StringBuilder对象 hexBuilder.setLength(0); for (byte b : bytes) { hexBuilder.append(String.format("%02X ", b)); } return hexBuilder.toString().trim(); } // è¾ å©æ¹æ³ï¼å°HEXå符串转æ¢ä¸ºåèæ°ç» private byte[] hexStringToByteArray(String s) { int len = s.length(); if (len % 2 != 0) { return null; } byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { try { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); } catch (Exception e) { return null; } } return data; } public void updateLanguage() { // æ´æ°çé¢ææ¬ openSerialBtn.setText(serialOpened ? getString("close_serial") : getString("open_serial")); startPauseBtn.setText(isRunning ? getString("pause") : getString("start")); clearBtn.setText(getString("clear")); sendBtn.setText(getString("send")); // æ´æ°æ ç¾ææ¬ displayFormatLabel.setText(getString("display_format") + ":"); serialPortLabel.setText(getString("serial_port") + ":"); baudRateLabel.setText(getString("baud_rate") + ":"); // æ´æ°åå columnNames[0] = getString("device_id_table"); columnNames[1] = getString("distance_table"); columnNames[2] = getString("angle_table"); columnNames[3] = getString("signal_table"); columnNames[4] = getString("power_table"); columnNames[5] = getString("button_table"); columnNames[6] = getString("time_table"); // æ´æ°æ¾ç¤ºæ ¼å¼åéæé® hexRadio.setText(getString("hex")); asciiRadio.setText(getString("ascii")); hexSendCheckBox.setText(getString("hex_send")); // æ´æ°è¾¹æ¡æ é¢ updateBorderTitles(this); dataTable.repaint(); } private void updateBorderTitles(Container container) { for (Component comp : container.getComponents()) { if (comp instanceof JPanel) { JPanel panel = (JPanel) comp; Border border = panel.getBorder(); if (border instanceof TitledBorder) { TitledBorder titledBorder = (TitledBorder) border; String title = titledBorder.getTitle(); if (title.equals("æ¥å¿") || title.equals("Log")) { titledBorder.setTitle(getString("log")); } else if (title.equals(getString("device_id_table")) || title.equals("Device ID")) { titledBorder.setTitle(getString("device_id_table")); } else if (title.equals(getString("serial_port")) || title.equals("Serial Port")) { titledBorder.setTitle(getString("serial_port")); } else if (title.equals("åéæ°æ®") || title.equals("Send Data")) { titledBorder.setTitle(getString("send_data")); } else if (title.equals("å¯è§å") || title.equals("Visualization")) { titledBorder.setTitle(getString("visualization")); } panel.repaint(); } // é彿´æ°åç»ä»¶ updateBorderTitles(panel); } } } private String getString(String key) { if (parentFrame != null) { return parentFrame.getString(key); } else { // è¿åé»è®¤ææ¬ï¼ä¸æï¼æ key æ¬èº«ä½ä¸ºå¤ç¨ switch (key) { case "home": return "é¦é¡µ"; case "config": return "é ç½®"; case "display_format": return "æ¾ç¤ºæ ¼å¼"; case "serial_port": return "串å£"; case "baud_rate": return "æ³¢ç¹ç"; case "close_serial": return "å ³é串å£"; // å ¶ä» key çé»è®¤å¼... default: return key; } } } } } src/home/ConfigPanel.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,251 @@ package home; import javax.swing.*; import javax.swing.Timer; import java.awt.*; import java.awt.event.*; import java.io.File; public class ConfigPanel extends JPanel { private JTextField deviceIdField; private JTextField groupField; private JTextField frequencyField; private JButton readConfigBtn; private JButton saveConfigBtn; private JTextField filePathField; private JButton selectFileBtn; private JButton upgradeBtn; private JProgressBar progressBar; private SerialPortService serialService; private FirmwareUpgrader firmwareUpgrader; private AOAFollowSystem parentFrame; // ç¨äºç颿æ¬çæ ç¾ private JLabel deviceIdLabel; private JLabel groupLabel; private JLabel frequencyLabel; private JLabel selectFileLabel; public ConfigPanel(SerialPortService serialService, AOAFollowSystem parentFrame) { this.serialService = serialService; this.parentFrame = parentFrame; this.firmwareUpgrader = new FirmwareUpgrader(serialService); setLayout(new GridBagLayout()); setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(10, 10, 10, 10); gbc.fill = GridBagConstraints.HORIZONTAL; // 设å¤ç¼å· - 使ç¨getString()æ¹æ³ gbc.gridx = 0; gbc.gridy = 0; deviceIdLabel = new JLabel(getString("device_id") + ":"); add(deviceIdLabel, gbc); gbc.gridx = 1; deviceIdField = new JTextField(15); deviceIdField.setText("2548"); add(deviceIdField, gbc); // éä¿¡å°ç» - 使ç¨getString()æ¹æ³ gbc.gridx = 0; gbc.gridy = 1; groupLabel = new JLabel(getString("group") + ":"); add(groupLabel, gbc); gbc.gridx = 1; groupField = new JTextField(15); groupField.setText("2"); add(groupField, gbc); // éä¿¡é¢ç - 使ç¨getString()æ¹æ³ gbc.gridx = 0; gbc.gridy = 2; frequencyLabel = new JLabel(getString("frequency") + ":"); add(frequencyLabel, gbc); gbc.gridx = 1; frequencyField = new JTextField(15); frequencyField.setText("1"); add(frequencyField, gbc); // éæ©æä»¶ - 使ç¨getString()æ¹æ³ gbc.gridx = 0; gbc.gridy = 3; selectFileLabel = new JLabel(getString("select_file") + ":"); add(selectFileLabel, gbc); gbc.gridx = 1; JPanel filePanel = new JPanel(new BorderLayout(5, 0)); filePathField = new JTextField(); filePathField.setEditable(false); selectFileBtn = new JButton("..."); selectFileBtn.setPreferredSize(new Dimension(30, 25)); selectFileBtn.addActionListener(e -> selectFile()); filePanel.add(filePathField, BorderLayout.CENTER); filePanel.add(selectFileBtn, BorderLayout.EAST); add(filePanel, gbc); // åºä»¶å级 gbc.gridx = 0; gbc.gridy = 4; gbc.gridwidth = 2; upgradeBtn = createColoredButton(getString("upgrade"), new Color(70, 130, 180)); upgradeBtn.setPreferredSize(new Dimension(100, 30)); upgradeBtn.addActionListener(e -> upgradeFirmware()); add(upgradeBtn, gbc); // è¿åº¦æ¡ gbc.gridx = 0; gbc.gridy = 5; gbc.gridwidth = 2; progressBar = new JProgressBar(0, 100); progressBar.setStringPainted(true); progressBar.setVisible(false); add(progressBar, gbc); // æä½æé® gbc.gridx = 0; gbc.gridy = 6; gbc.gridwidth = 2; JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); readConfigBtn = createColoredButton(getString("read_config"), new Color(70, 130, 180)); readConfigBtn.setPreferredSize(new Dimension(100, 30)); saveConfigBtn = createColoredButton(getString("save_config"), new Color(50, 205, 50)); saveConfigBtn.setPreferredSize(new Dimension(100, 30)); readConfigBtn.addActionListener(e -> readConfig()); saveConfigBtn.addActionListener(e -> saveConfig()); buttonPanel.add(readConfigBtn); buttonPanel.add(saveConfigBtn); add(buttonPanel, gbc); } private JButton createColoredButton(String text, Color color) { JButton button = new JButton(text); button.setBackground(color); button.setForeground(Color.WHITE); button.setFocusPainted(false); button.setBorder(BorderFactory.createEmptyBorder(8, 15, 8, 15)); button.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { button.setBackground(color.darker()); } @Override public void mouseExited(MouseEvent e) { button.setBackground(color); } }); return button; } private void selectFile() { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("BIN Files", "bin")); int result = fileChooser.showOpenDialog(this); if (result == JFileChooser.APPROVE_OPTION) { File selectedFile = fileChooser.getSelectedFile(); filePathField.setText(selectedFile.getAbsolutePath()); } } private void upgradeFirmware() { String filePath = filePathField.getText(); if (filePath.isEmpty()) { JOptionPane.showMessageDialog(this, getString("select_bin_file"), getString("error"), JOptionPane.ERROR_MESSAGE); return; } if (!serialService.isOpen()) { JOptionPane.showMessageDialog(this, getString("open_serial_first"), getString("error"), JOptionPane.ERROR_MESSAGE); return; } // è¿è¡åçº§ï¼æ¾ç¤ºè¿åº¦æ¡ upgradeBtn.setEnabled(false); selectFileBtn.setEnabled(false); progressBar.setVisible(true); progressBar.setValue(0); // 卿°çº¿ç¨ä¸æ§è¡å级æä½ new Thread(() -> { try { firmwareUpgrader.upgradeFirmware(filePath, progress -> { SwingUtilities.invokeLater(() -> { progressBar.setValue(progress); }); }); SwingUtilities.invokeLater(() -> { JOptionPane.showMessageDialog(this, getString("upgrade_success"), getString("info"), JOptionPane.INFORMATION_MESSAGE); upgradeBtn.setEnabled(true); selectFileBtn.setEnabled(true); }); } catch (Exception e) { SwingUtilities.invokeLater(() -> { JOptionPane.showMessageDialog(this, getString("upgrade_failed") + ": " + e.getMessage(), getString("error"), JOptionPane.ERROR_MESSAGE); upgradeBtn.setEnabled(true); selectFileBtn.setEnabled(true); }); } }).start(); } private void readConfig() { // 模æè¯»åé ç½® readConfigBtn.setBackground(Color.GREEN); Timer timer = new Timer(500, e -> { readConfigBtn.setBackground(new Color(70, 130, 180)); }); timer.setRepeats(false); timer.start(); JOptionPane.showMessageDialog(this, getString("config_read_success"), getString("info"), JOptionPane.INFORMATION_MESSAGE); } private void saveConfig() { // éªè¯è¾å ¥ try { Integer.parseInt(deviceIdField.getText()); Integer.parseInt(groupField.getText()); Integer.parseInt(frequencyField.getText()); } catch (NumberFormatException e) { JOptionPane.showMessageDialog(this, getString("input_valid_number"), getString("error"), JOptionPane.ERROR_MESSAGE); return; } // 模æä¿åé ç½® saveConfigBtn.setBackground(Color.GREEN); Timer timer = new Timer(500, e -> { saveConfigBtn.setBackground(new Color(50, 205, 50)); }); timer.setRepeats(false); timer.start(); JOptionPane.showMessageDialog(this, getString("config_save_success"), getString("info"), JOptionPane.INFORMATION_MESSAGE); } public void updateLanguage() { // æ´æ°æé®ææ¬ readConfigBtn.setText(getString("read_config")); saveConfigBtn.setText(getString("save_config")); upgradeBtn.setText(getString("upgrade")); // æ´æ°æ ç¾ææ¬ deviceIdLabel.setText(getString("device_id") + ":"); groupLabel.setText(getString("group") + ":"); frequencyLabel.setText(getString("frequency") + ":"); selectFileLabel.setText(getString("select_file") + ":"); } private String getString(String key) { return parentFrame.getString(key); } } src/home/Dell55AA01Parser.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,111 @@ package home; import java.util.Arrays; 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 int buttonPressed; // æé®ç¶æ public boolean buttonPressed2; public void reset() { sequenceNum = 0; tagId = ""; anchorId = ""; distance = 0; power = 0; buttonPressed = 0; buttonPressed2=false; } } /** * è§£æ55AA01åè®®æ°æ® * @param message åå è¿å¶å符串 * @return è§£æç»æ(é误æ¶è¿ånull) */ public static ParseResult parse(String message, String ip, int port) { if (message == null || message.isEmpty()) { return null; } // æ¸ æ´æ°æ®ï¼ç§»é¤ææéåå è¿å¶å符 char[] cleanedMessage = cleanMessage(message); // æ°æ®æ ¡éª if (cleanedMessage == null || cleanedMessage.length < MIN_LENGTH) { return null; } // åè®®å¤´æ ¡éª (55AA01) if (!new String(cleanedMessage, 0, 6).equals(EXPECTED_HEADER)) { return null; } ParseResult result = RESULT_CACHE.get(); result.reset(); try { if (cleanedMessage.length < 30) { // ç¡®ä¿æè¶³å¤é¿åº¦è®¿é®charAt(28) return null; } // è§£æåºåå· (ä½ç½®8-9) result.sequenceNum = HexUtils.fastHexToByte(cleanedMessage[8], cleanedMessage[9]); // è§£ææ ç¾ID (ä½ç½®10-13, å°ç«¯åº) result.tagId = new String(new char[]{ cleanedMessage[12], cleanedMessage[13], // é«ä½ cleanedMessage[10], cleanedMessage[11] // ä½ä½ }); // è§£æéç¹ID (ä½ç½®14-17, å°ç«¯åº) result.anchorId = new String(new char[]{ cleanedMessage[16], cleanedMessage[17], // é«ä½ cleanedMessage[14], cleanedMessage[15] // ä½ä½ }); // è§£æè·ç¦» (ä½ç½®18-25, 4åèå°ç«¯æ´æ°) int b0 = HexUtils.fastHexToByte(cleanedMessage[18], cleanedMessage[19]); // æä½ä½ int b1 = HexUtils.fastHexToByte(cleanedMessage[20], cleanedMessage[21]); int b2 = HexUtils.fastHexToByte(cleanedMessage[22], cleanedMessage[23]); int b3 = HexUtils.fastHexToByte(cleanedMessage[24], cleanedMessage[25]); // æé«ä½ int raw = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; result.distance = raw; // ä¿æåå§æ´æ°å¼ // è§£æçµé (ä½ç½®26-27) result.power = HexUtils.fastHexToByte(cleanedMessage[26], cleanedMessage[27]); // è§£ææé®ç¶æ (ä½ç½®28-29) result.buttonPressed = HexUtils.fastHexToByte(cleanedMessage[28], cleanedMessage[29]); result.buttonPressed2 =result.buttonPressed==1; } catch (IndexOutOfBoundsException | NumberFormatException e) { System.err.println("Parsing error in packet from " + ip + ":" + port); return null; } return result; } private static char[] cleanMessage(String message) { char[] cleaned = new char[message.length()]; int j = 0; for (char c : message.toCharArray()) { if (Character.isDigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) { cleaned[j++] = Character.toUpperCase(c); } } if (j == 0) return null; return Arrays.copyOf(cleaned, j); } } src/home/Dell55AA1FParser.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,184 @@ package home; import java.util.Arrays; public class Dell55AA1FParser { // 常éå®ä¹ private static final String EXPECTED_HEADER = "55AA1F"; // å议头 private static final int FIXED_FIELDS_LENGTH = 11; // 仿 ç¾IDå°ä¿çåæ®µçåºå®é¿åº¦(åè) private static final ThreadLocal<ParseResult> RESULT_CACHE = ThreadLocal.withInitial(ParseResult::new); // è§£æç»æç±» public static class ParseResult { public int dataLength; // æ°æ®é¿åº¦ public int messageType; // æ¶æ¯ç±»å public String tagId; // æ ç¾ID(2åè) public int distance; // è·ç¦»(åç±³) public int angle; // è§åº¦(度) public int signalQuality; // ä¿¡å·è´¨é(0-255) public int buttonPressed; // æé®ç¶æ public int power; // çµé public String reserved; // ä¿çåæ®µ public String userData; // ç¨æ·æ°æ® public String checksum; // æ ¡éªå public void reset() { dataLength = 0; messageType = 0; tagId = ""; distance = 0; angle = 0; signalQuality = 0; buttonPressed = 0; power = 0; reserved = ""; userData = ""; checksum = ""; } } /** * è§£æ55AA1Fåè®®æ°æ® * @param message åå è¿å¶å符串 * @return è§£æç»æ(é误æ¶è¿ånull) */ public static ParseResult parse(String message, String ip, int port) { if (message == null || message.isEmpty()) { return null; } // æ¸ æ´æ°æ®ï¼ç§»é¤ææéåå è¿å¶å符 char[] cleanedMessage = cleanMessage(message); if (cleanedMessage == null) { return null; } // åè®®å¤´æ ¡éª (55AA1F) if (cleanedMessage.length < 6 || !new String(cleanedMessage, 0, 6).equals(EXPECTED_HEADER)) { return null; } ParseResult result = RESULT_CACHE.get(); result.reset(); try { // è§£ææ°æ®é¿åº¦ (ä½ç½®6-7) result.dataLength = HexUtils.fastHexToByte(cleanedMessage[6], cleanedMessage[7]); // è®¡ç®ææçæ»å符é¿åº¦ int expectedCharLength = 8 + // å 头(4å符) + æ¶æ¯ç±»å(2å符) + æ°æ®é¿åº¦(2å符) result.dataLength * 2 + // æ°æ®é¨å 4; // æ ¡éªå(4å符) // if (cleanedMessage.length != expectedCharLength) { // System.err.println("Data length mismatch: expected " + expectedCharLength + // ", got " + cleanedMessage.length); // return null; // } // è§£ææ¶æ¯ç±»å (ä½ç½®4-5) result.messageType = HexUtils.fastHexToByte(cleanedMessage[4], cleanedMessage[5]); // è§£ææ ç¾ID (ä½ç½®8-11)ï¼ç´æ¥åå符串 result.tagId = new String(cleanedMessage, 8, 4); // è§£æè·ç¦» (ä½ç½®12-15, 2åèå°ç«¯æ´æ°) int distLow = HexUtils.fastHexToByte(cleanedMessage[12], cleanedMessage[13]); int distHigh = HexUtils.fastHexToByte(cleanedMessage[14], cleanedMessage[15]); result.distance = (distHigh << 8) | distLow; // è§£æè§åº¦ (ä½ç½®16-19, 2åèå°ç«¯æç¬¦å·æ´æ°) int angleLow = HexUtils.fastHexToByte(cleanedMessage[16], cleanedMessage[17]); int angleHigh = HexUtils.fastHexToByte(cleanedMessage[18], cleanedMessage[19]); short angleShort = (short) ((angleHigh << 8) | angleLow); result.angle = angleShort; // è§£æä¿¡å·è´¨é (ä½ç½®20-21) result.signalQuality = HexUtils.fastHexToByte(cleanedMessage[20], cleanedMessage[21]); // è§£ææé®ç¶æ (ä½ç½®22-23) result.buttonPressed = HexUtils.fastHexToByte(cleanedMessage[22], cleanedMessage[23]); // è§£æçµé (ä½ç½®24-25) result.power = HexUtils.fastHexToByte(cleanedMessage[24], cleanedMessage[25]); // è§£æä¿çåæ®µ (ä½ç½®26-29) result.reserved = new String(cleanedMessage, 26, 4); // è§£æç¨æ·æ°æ® // int userDataStart = 30; // int userDataCharLength = (result.dataLength - FIXED_FIELDS_LENGTH) * 2; // if (userDataCharLength > 0) { // result.userData = new String(cleanedMessage, userDataStart, userDataCharLength); // } else { // result.userData = ""; // } // è§£ææ ¡éªå (æå4个å符) result.checksum = new String(cleanedMessage, cleanedMessage.length - 4, 4); // éªè¯æ ¡éªå // byte[] packetBytes = hexStringToByteArray(new String(cleanedMessage)); // if (!verifyChecksum(packetBytes)) { // System.err.println("Checksum verification failed for packet from " + ip + ":" + port); // return null; // } } catch (IndexOutOfBoundsException | NumberFormatException e) { System.err.println("Parsing error in 55AA1F packet from " + ip + ":" + port); e.printStackTrace(); return null; } return result; } /** * å°åå è¿å¶å符串转æ¢ä¸ºåèæ°ç» */ private static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); } return data; } /** * æ ¡éªæ°æ®å * 廿å 头æ±åååï¼æ ¡éªç 为å°ç«¯æ¨¡å¼ */ private static boolean verifyChecksum(byte[] packet) { int len = packet.length; if (len < 4) return false; int sum = 0; // 仿¶æ¯ç±»åå¼å§å°æ ¡éªç åç»æ (è·³è¿å 头2åè) for (int i = 2; i < len - 2; i++) { sum += packet[i] & 0xFF; } sum = ~sum & 0xFFFF; // ååå¹¶ä¿ç16ä½ // è·åå ä¸çæ ¡éªç (å°ç«¯æ¨¡å¼) int receivedChecksum = ((packet[len - 2] & 0xFF) << 8) | (packet[len - 1] & 0xFF); return sum == receivedChecksum; } private static char[] cleanMessage(String message) { char[] cleaned = new char[message.length()]; int j = 0; for (char c : message.toCharArray()) { if (Character.isDigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) { cleaned[j++] = Character.toUpperCase(c); } } if (j == 0) return null; return Arrays.copyOf(cleaned, j); } } src/home/FirmwareUpgrader.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,216 @@ package home; import java.io.*; import java.util.Arrays; import java.util.function.Consumer; public class FirmwareUpgrader { private static final int SOH = 0x01; // Start of Header private static final int STX = 0x02; // Start of Text (for 1024-byte blocks) private static final int EOT = 0x04; // End of Transmission private static final int ACK = 0x06; // Acknowledge private static final int NAK = 0x15; // Negative Acknowledge private static final int CAN = 0x18; // Cancel private static final int C = 0x43; // 'C' for CRC mode private final SerialPortService serialService; public FirmwareUpgrader(SerialPortService serialService) { this.serialService = serialService; } public void upgradeFirmware(String filePath, ProgressCallback progressCallback) throws IOException { File file = new File(filePath); if (!file.exists() || !file.isFile()) { throw new IOException("File not found: " + filePath); } // åéå¼å§ä¿¡å· sendStartSignal(); // åéæä»¶ sendFile(file, progressCallback); // åéç»æä¿¡å· sendEndSignal(); } private void sendStartSignal() throws IOException { // åé'C'表示使ç¨CRCæ ¡éª serialService.send(new byte[]{C}); // çå¾ ACK if (!waitForAck()) { throw new IOException("Device not ready for YModem transfer"); } } private void sendFile(File file, ProgressCallback progressCallback) throws IOException { long fileSize = file.length(); String fileName = file.getName(); // åéæä»¶å¤´å byte[] header = new byte[133]; Arrays.fill(header, (byte)0); header[0] = SOH; header[1] = 0x00; // åç¼å· header[2] = (byte)0xFF; // åç¼å·è¡¥ç // æä»¶ååå¤§å° byte[] fileNameBytes = fileName.getBytes(); System.arraycopy(fileNameBytes, 0, header, 3, Math.min(fileNameBytes.length, 99)); String sizeStr = String.valueOf(fileSize); byte[] sizeBytes = sizeStr.getBytes(); System.arraycopy(sizeBytes, 0, header, 3 + 99, Math.min(sizeBytes.length, 99)); // 计ç®CRC int crc = calculateCRC(header, 3, 128); header[131] = (byte)((crc >> 8) & 0xFF); header[132] = (byte)(crc & 0xFF); serialService.send(header); // çå¾ ACK if (!waitForAck()) { throw new IOException("Header not acknowledged"); } // åéæä»¶æ°æ® try (FileInputStream fis = new FileInputStream(file)) { byte[] buffer = new byte[1024]; int bytesRead; int blockNum = 1; long totalSent = 0; while ((bytesRead = fis.read(buffer)) != -1) { // å¡«å ä¸è¶³çé¨å if (bytesRead < 1024) { Arrays.fill(buffer, bytesRead, 1024, (byte)0x1A); } // åéæ°æ®å sendDataBlock(blockNum, buffer); blockNum++; totalSent += bytesRead; // æ´æ°è¿åº¦ int progress = (int)((totalSent * 100) / fileSize); progressCallback.onProgress(progress); // çå¾ ACK if (!waitForAck()) { throw new IOException("Data block not acknowledged"); } } } } private void sendDataBlock(int blockNum, byte[] data) throws IOException { byte[] block = new byte[1029]; block[0] = STX; // 1024åèå block[1] = (byte)(blockNum & 0xFF); block[2] = (byte)(~blockNum & 0xFF); // æ·è´æ°æ® System.arraycopy(data, 0, block, 3, data.length); // 计ç®CRC int crc = calculateCRC(block, 3, 1024); block[1027] = (byte)((crc >> 8) & 0xFF); block[1028] = (byte)(crc & 0xFF); serialService.send(block); } private void sendEndSignal() throws IOException { // åéEOT serialService.send(new byte[]{EOT}); // çå¾ ACK if (!waitForAck()) { throw new IOException("EOT not acknowledged"); } // åé空æä»¶å¤´åè¡¨ç¤ºç»æ byte[] endHeader = new byte[133]; Arrays.fill(endHeader, (byte)0); endHeader[0] = SOH; endHeader[1] = 0x00; // åç¼å· endHeader[2] = (byte)0xFF; // åç¼å·è¡¥ç // 计ç®CRC int crc = calculateCRC(endHeader, 3, 128); endHeader[131] = (byte)((crc >> 8) & 0xFF); endHeader[132] = (byte)(crc & 0xFF); serialService.send(endHeader); // çå¾ ACK if (!waitForAck()) { throw new IOException("End header not acknowledged"); } } private boolean waitForAck() { final Object lock = new Object(); final boolean[] result = new boolean[1]; final boolean[] receivedResponse = new boolean[1]; // å建ä¸ä¸ªä¸´æ¶æ¶è´¹è æ¥å¤çæ¥æ¶å°çæ°æ® Consumer<byte[]> ackConsumer = data -> { if (data.length > 0) { synchronized (lock) { if (data[0] == ACK) { result[0] = true; receivedResponse[0] = true; } else if (data[0] == NAK) { result[0] = false; receivedResponse[0] = true; } else if (data[0] == CAN) { throw new RuntimeException("Transfer canceled by device"); } lock.notifyAll(); } } }; // 注åä¸´æ¶æ¶è´¹è serialService.setResponseConsumer(ackConsumer); long startTime = System.currentTimeMillis(); synchronized (lock) { while (System.currentTimeMillis() - startTime < 5000 && !receivedResponse[0]) { try { lock.wait(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } } // åæ¶æ³¨åä¸´æ¶æ¶è´¹è serialService.setResponseConsumer(null); return receivedResponse[0] && result[0]; } private int calculateCRC(byte[] data, int offset, int length) { int crc = 0; for (int i = 0; i < length; i++) { crc = crc ^ (data[offset + i] << 8); for (int j = 0; j < 8; j++) { if ((crc & 0x8000) != 0) { crc = (crc << 1) ^ 0x1021; } else { crc = crc << 1; } } } return crc & 0xFFFF; } public interface ProgressCallback { void onProgress(int progress); } } src/home/HexUtils.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,40 @@ package home; 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/home/LogUtil.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,87 @@ package home; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Date; // ä¿®å¤ï¼ä½¿ç¨ java.util.Date è䏿¯ java.sql.Date import java.text.SimpleDateFormat; /** * æ¥å¿è®°å½å·¥å ·ç±» */ public class LogUtil { private static final String LOG_DIR = "systemfile/logfile"; private static final String LOG_FILE = LOG_DIR + "/openlog.txt"; private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * è®°å½ç¨åºå¯å¨æ¥å¿ */ public static void logOpen() { ensureLogDirExists(); String timestamp = DATE_FORMAT.format(new Date()); writeLog("ç¨åºå¯å¨: " + timestamp + "\n"); } /** * è®°å½ç¨åºå ³éæ¥å¿å¹¶è®¡ç®å·¥ä½æ¶é¿ * @param startTime ç¨åºå¯å¨æ¶é´æ³ */ public static void logClose(long startTime) { ensureLogDirExists(); long endTime = System.currentTimeMillis(); String closeTime = DATE_FORMAT.format(new Date(endTime)); // 计ç®å·¥ä½æ¶é¿ long duration = endTime - startTime; String durationStr = formatDuration(duration); String log = "ç¨åºå ³é: " + closeTime + "\n" + "工使¶é¿: " + durationStr + "\n" + "-----------------------------------\n"; writeLog(log); } /** * ç¡®ä¿æ¥å¿ç®å½åå¨ */ private static void ensureLogDirExists() { File dir = new File(LOG_DIR); if (!dir.exists()) { boolean created = dir.mkdirs(); // å建å¤çº§ç®å½ if (!created) { System.err.println("æ æ³å建æ¥å¿ç®å½: " + LOG_DIR); } } } /** * æ ¼å¼åæ¶é¿ï¼æ¯«ç§âå¯è¯»åç¬¦ä¸²ï¼ */ private static String formatDuration(long millis) { long seconds = millis / 1000; long hours = seconds / 3600; long minutes = (seconds % 3600) / 60; seconds = seconds % 60; return String.format("%då°æ¶ %dåé %dç§", hours, minutes, seconds); } /** * åå ¥æ¥å¿å°æä»¶ï¼è¿½å 模å¼ï¼ */ private static void writeLog(String content) { try (FileWriter fw = new FileWriter(LOG_FILE, true); BufferedWriter bw = new BufferedWriter(fw)) { bw.write(content); bw.flush(); // ç¡®ä¿å 容ç«å³åå ¥ } catch (IOException e) { System.err.println("åå ¥æ¥å¿å¤±è´¥: " + e.getMessage()); } } public static void log(String string) { // TODO èªå¨çæçæ¹æ³åæ ¹ } } src/home/Mains.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,44 @@ package home; import javax.swing.SwingUtilities; public class Mains { // è®°å½å¯å¨æ¶é´ private static long startTime; public static void main(String[] args) { // è®°å½å¯å¨æ¶é´ startTime = System.currentTimeMillis(); // è®°å½æå¼æ¥å¿ LogUtil.logOpen(); if (SingleInstanceLock.lock()) { try { // å¨äºä»¶è°åº¦çº¿ç¨ä¸åå»ºåæ¾ç¤ºGUI SwingUtilities.invokeLater(() -> { try { // å建并æ¾ç¤ºä¸»çé¢ AOAFollowSystem system = new AOAFollowSystem(); system.setVisible(true); } catch (Exception e) { e.printStackTrace(); LogUtil.log("å¯å¨å¯è§åç颿¶åçé误: " + e.getMessage()); } }); } finally { // æ·»å å ³éé©å Runtime.getRuntime().addShutdownHook(new Thread(() -> { // è®°å½å ³éæ¥å¿ LogUtil.logClose(startTime); // éæ¾åå®ä¾é SingleInstanceLock.release(); })); } } else { // æ¾ç¤ºç¨åºå·²å¨è¿è¡çè¦å SingleInstanceLock.showAlreadyRunningWarning(); // éåºç¨åº System.exit(0); } } } src/home/SerialPortService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,171 @@ // 声æå å dell_system package home; // å¯¼å ¥ä¸²å£éä¿¡åº 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; private Consumer<byte[]> responseConsumer; // ä¼åï¼éç¨ç¼å²åºï¼åå°å ååé private byte[] readBuffer = new byte[200]; private ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024); /** * æå¼ä¸²å£ * @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 setResponseConsumer(Consumer<byte[]> consumer) { this.responseConsumer = consumer; } /** * å ³éä¸²å£ */ 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(() -> { buffer.reset(); long lastReceivedTime = 0; while (capturing && port.isOpen()) { if (paused) { buffer.reset(); int len; do { len = port.readBytes(readBuffer, readBuffer.length); } while (len > 0); try { Thread.sleep(100); } catch (InterruptedException ignore) {} continue; } long currentTime = System.currentTimeMillis(); if (buffer.size() > 0 && (currentTime - lastReceivedTime) >= 20) { byte[] data = buffer.toByteArray(); onReceived.accept(data); if (responseConsumer != null) { responseConsumer.accept(data); } buffer.reset(); } int len = port.readBytes(readBuffer, readBuffer.length); currentTime = System.currentTimeMillis(); if (len > 0) { buffer.write(readBuffer, 0, len); lastReceivedTime = currentTime; } if (len <= 0 && buffer.size() == 0) { try { Thread.sleep(1); } catch (InterruptedException ignore) {} } } if (buffer.size() > 0) { byte[] data = buffer.toByteArray(); onReceived.accept(data); if (responseConsumer != null) { responseConsumer.accept(data); } } }); 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/home/SingleInstanceLock.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,77 @@ package home; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import javax.swing.JOptionPane; public class SingleInstanceLock { private static final String LOCK_FILE = System.getProperty("user.home") + File.separator + ".myapp.lock"; private static FileLock lock; private static FileChannel channel; /** * å°è¯è·ååºç¨ç¨åºé * @return true-æåè·åéï¼ç¬¬ä¸æ¬¡è¿è¡ï¼, false-è·å失败ï¼å·²æå®ä¾è¿è¡ï¼ */ @SuppressWarnings("resource") public static boolean lock() { try { // å建æä»¶å¯¹è±¡ File file = new File(LOCK_FILE); // 妿æä»¶åå¨ä½æ æ³å é¤ï¼å¯è½æ¯å ¶ä»å®ä¾æ£å¨è¿è¡ if (file.exists() && !file.delete()) { return false; } // å建æä»¶å¹¶è·åéé RandomAccessFile raf = new RandomAccessFile(file, "rw"); channel = raf.getChannel(); // å°è¯è·åç¬å é lock = channel.tryLock(); // 妿è·åå°éï¼è¿åtrue return lock != null; } catch (IOException e) { // åçå¼å¸¸è¯´æå¯è½å·²æå®ä¾è¿è¡ return false; } } /** * éæ¾åºç¨ç¨åºé */ public static void release() { try { // éæ¾é if (lock != null && lock.isValid()) { lock.release(); } // å ³ééé if (channel != null && channel.isOpen()) { channel.close(); } // å é¤éæä»¶ File file = new File(LOCK_FILE); if (file.exists()) { file.delete(); } } catch (IOException e) { // éæ¾éæ¶å¿½ç¥å¼å¸¸ } } /** * æ£æ¥ç¨åºæ¯å¦å·²å¨è¿è¡å¹¶æ¾ç¤ºè¦å */ public static void showAlreadyRunningWarning() { JOptionPane.showMessageDialog(null, "ç¨åºå·²ç»æå¼ï¼è¯·å¿é夿å¼", "è¦å", JOptionPane.WARNING_MESSAGE); } } src/home/VisualizationPanel.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,205 @@ package home; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import javax.swing.BorderFactory; import javax.swing.JPanel; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; // å¯è§å颿¿ç±» class VisualizationPanel extends JPanel { /** * */ private static final long serialVersionUID = 1L; private int distance = 0; private int angle = 0; private AOAFollowSystem parentFrame; private String anchorid = "Anchor"; private String tagid = "Tag"; private double scaleFactor = 1.0; // 缩æ¾å å private static final double MIN_SCALE = 0.2; // æå°ç¼©æ¾åæ° private static final double MAX_SCALE = 5.0; // æå¤§ç¼©æ¾åæ° // ä½¿ç¨æ¯æä¸æçåä½ private Font boldFont = new Font("Microsoft YaHei", Font.BOLD, 14); private Font normalFont = new Font("Microsoft YaHei", Font.PLAIN, 12); private Font titleFont = new Font("Microsoft YaHei", Font.BOLD, 12); public VisualizationPanel(AOAFollowSystem parentFrame) { this.parentFrame = parentFrame; setPreferredSize(new Dimension(400, 400)); setBorder(BorderFactory.createTitledBorder(parentFrame.getString("visualization"))); // æ·»å é¼ æ æ»è½®çå¬å¨ addMouseWheelListener(new MouseWheelListener() { @Override public void mouseWheelMoved(MouseWheelEvent e) { int notches = e.getWheelRotation(); if (notches < 0) { // å䏿»å¨ï¼æ¾å¤§ scaleFactor = Math.min(scaleFactor * 1.1, MAX_SCALE); } else { // å䏿»å¨ï¼ç¼©å° scaleFactor = Math.max(scaleFactor / 1.1, MIN_SCALE); } repaint(); } }); } public void updatePosition(int distance, int angle) { // ä¼åï¼åªææ°æ®ååæ¶æéç» if (this.distance != distance || this.angle != angle) { this.distance = distance; // å°è§åº¦èå´è½¬æ¢ä¸º-180å°180åº¦ï¼æ£ä¸æ¹ä¸º0度 this.angle = normalizeAngle(angle); repaint(); } } // è§åº¦å½ä¸åæ¹æ³ï¼å°è§åº¦è½¬æ¢ä¸º-180å°180度èå´ï¼æ£ä¸æ¹ä¸º0度 private int normalizeAngle(int angle) { // é¦å å°è§åº¦è½¬æ¢ä¸º0-360度èå´ angle = angle % 360; if (angle < 0) { angle += 360; } // å°0-360度转æ¢ä¸º-180å°180åº¦ï¼æ£ä¸æ¹ä¸º0度 // 0åº¦å¯¹åºæ£ä¸æ¹ï¼90åº¦å¯¹åºæ£å·¦æ¹ï¼180/-180åº¦å¯¹åºæ£ä¸æ¹ï¼-90åº¦å¯¹åºæ£å³æ¹ int normalizedAngle = angle - 180; if (normalizedAngle > 180) { normalizedAngle -= 360; } else if (normalizedAngle <= -180) { normalizedAngle += 360; } return normalizedAngle; } public void setAnchorId(String anchorid) { this.anchorid = anchorid; } public void setTagId(String tagid) { this.tagid = tagid; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // ç¡®ä¿ä½¿ç¨æ¯æä¸æçåä½ g2d.setFont(normalFont); int centerX = getWidth() / 2; int centerY = getHeight()/ 2 - 30; int maxRadius = (int) (Math.min(centerX, centerY) * scaleFactor - 20); // è®¾ç½®èæ¯é¢è² g2d.setColor(new Color(240, 240, 240)); g2d.fillRect(0, 0, getWidth(), getHeight()); // ç»å¶åæ è½´ g2d.setColor(Color.LIGHT_GRAY); g2d.drawLine(centerX, 0, centerX, getHeight()); g2d.drawLine(0, centerY, getWidth(), centerY); // ç»å¶è§åº¦å»åº¦çº¿ (-180° å° 180°) g2d.setColor(new Color(150, 150, 150)); for (int i = -180; i <= 180; i += 30) { // 转æ¢ä¸ºå¼§åº¦ï¼æ£ä¸æ¹ä¸º0度 double rad = Math.toRadians(i); int x1 = centerX + (int) (maxRadius * Math.sin(rad)); int y1 = centerY - (int) (maxRadius * Math.cos(rad)); g2d.setColor(new Color(100, 100, 100)); g2d.drawLine(centerX, centerY, x1, y1); // ç»å¶è§åº¦æ ç¾ int labelX = centerX + (int) ((maxRadius + 15) * Math.sin(rad)); int labelY = centerY - (int) ((maxRadius + 15) * Math.cos(rad)); g2d.setColor(Color.blue); g2d.drawString(i + "°", labelX - 10, labelY + 5); } // ç»å¶è·ç¦»åååæ ç¾ g2d.setColor(new Color(100, 100, 100)); for (int i = 1; i <= 5; i++) { int radius = maxRadius * i / 5; g2d.drawOval(centerX - radius, centerY - radius, radius * 2, radius * 2); // ä¿®æ¹ï¼å°è·ç¦»æ ç¾æ¾ç¤ºå¨å忣忹ä¸é´ String distanceLabel = (int)(i * 200 / scaleFactor) + "cm"; int labelWidth = g2d.getFontMetrics().stringWidth(distanceLabel); g2d.drawString(distanceLabel, centerX - labelWidth / 2, centerY + radius + 15); } // ç»å¶Aç¹ï¼éç¹ï¼ g2d.setColor(Color.BLACK); g2d.fillOval(centerX - 5, centerY - 5, 10, 10); // Aç¹æ£ä¸æ¹æ¾ç¤ºè®¾å¤ç¼å· g2d.setFont(titleFont); g2d.drawString(anchorid, centerX - g2d.getFontMetrics().stringWidth(anchorid)/2, centerY - 15); g2d.setFont(normalFont); // ç»å¶Bç¹ï¼æ ç¾ç¹ï¼ if (distance > 0) { // æ ¹æ®è·ç¦»è®¡ç®ç¼©æ¾æ¯ä¾ï¼æå¤§1000åç±³ï¼ double scaledDistance = Math.min(distance, 1000) * maxRadius / 1000.0; double rad = Math.toRadians(angle); int bX = centerX + (int) (scaledDistance * Math.sin(rad)); int bY = centerY - (int) (scaledDistance * Math.cos(rad)); // ç»å¶è¿æ¥çº¿ g2d.setColor(new Color(0, 100, 200, 150)); g2d.drawLine(centerX, centerY, bX, bY); // Bç¹æ£ä¸æ¹æ¾ç¤ºè·ç¦»åè§åº¦ä¿¡æ¯ String distanceText = distance + "cm"; String angleText = angle + "°"; // 设置å ç²åä½åå 大åå· g2d.setFont(boldFont); g2d.setColor(Color.RED); // è®¡ç®ææ¬ä½ç½®ï¼Bç¹æ£ä¸æ¹ï¼ int textY = bY + 25; g2d.drawString(distanceText, bX - g2d.getFontMetrics().stringWidth(distanceText)/2, textY); g2d.drawString(angleText, bX - g2d.getFontMetrics().stringWidth(angleText)/2, textY + 20); // æ¢å¤åå§åä½ g2d.setFont(normalFont); // å¨é¢æ¿åºé¨æ¾ç¤ºè·ç¦»åè§åº¦ä¿¡æ¯ - 宿¶è·åå符串ï¼ä¸ä½¿ç¨ç¼å g2d.setFont(boldFont); // ç´æ¥ä»ç¶æ¡æ¶è·åå½åè¯è¨çå符串 g2d.drawString(parentFrame.getString("distance") + distance + "cm", 10, 40); g2d.drawString(parentFrame.getString("angle") + angle + "°", 10, 70); g2d.setFont(normalFont); g2d.setColor(Color.BLUE); g2d.fillOval(bX - 5, bY - 5, 10, 10); // Bç¹æ£ä¸æ¹æ¾ç¤ºè®¾å¤ç¼å· g2d.setFont(titleFont); g2d.drawString(tagid, bX - g2d.getFontMetrics().stringWidth(tagid)/2, bY - 15); g2d.setFont(normalFont); } // æ¾ç¤ºå½åç¼©æ¾æ¯ä¾ g2d.setColor(Color.DARK_GRAY); g2d.drawString(String.format("缩æ¾: %.1fx", scaleFactor), getWidth() - 80, 20); } public void updateLanguage() { setBorder(BorderFactory.createTitledBorder(parentFrame.getString("visualization"))); // ç§»é¤å符串ç¼åï¼æ¹ä¸ºå¨paintComponentä¸å®æ¶è·å repaint(); } } systemfile/Messages_en.properties
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,51 @@ angle=Angle: distance=Distance: LANGUAGE=Language display_format=Display Format serial_port=Serial Port baud_rate=Baud Rate close_serial=Close Serial home=Home config=Configuration device_id=Device ID group=Communication Group frequency=Communication Frequency read_config=Read Configuration save_config=Save Configuration serial_port=Serial Port baud_rate=Baud Rate open_serial=Open Serial Port start=Start pause=Pause clear=Clear send=Send device_id_table=Device ID distance_table=Real-time Distance angle_table=Real-time Angle signal_table=Signal Quality power_table=Power Level button_table=Button time_table=Time log=Log send_data=Send Data select_serial_port=Please select a serial port error=Error open_serial_first=Please open the serial port first input_data_to_send=Please input data to send send_failed=Send failed receive=Receive visualization=Visualization config_read_success=Configuration read successfully info=Information input_valid_number=Please input valid numbers config_save_success=Configuration saved successfully select_file=Select File upgrade=Upgrade upgrade_progress=Upgrade Progress select_bin_file=Please select bin file upgrade_success=Upgrade Success upgrade_failed=Upgrade Failed hex=HEX ascii=ASCII hex_send=HEX Send close_serial=Close Serial systemfile/Messages_zh.properties
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,51 @@ angle=è§åº¦ï¼ distance=è·ç¦»ï¼ LANGUAGE=è¯è¨ display_format=æ¾ç¤ºæ ¼å¼ serial_port=ä¸²å£ baud_rate=æ³¢ç¹ç close_serial=å ³éä¸²å£ home=é¦é¡µ config=é ç½® device_id=设å¤ç¼å· group=éä¿¡å°ç» frequency=éä¿¡é¢ç read_config=读åé ç½® save_config=ä¿åé ç½® serial_port=ä¸²å£ baud_rate=æ³¢ç¹ç open_serial=æå¼ä¸²å£ start=å¼å§ pause=æå clear=æ¸ ç©º send=åé device_id_table=设å¤ç¼å· distance_table=宿¶è·ç¦» angle_table=宿¶è§åº¦ signal_table=ä¿¡å·è´¨é power_table=çµé button_table=æé® time_table=æ¶é´ log=æ¥å¿ send_data=åéæ°æ® select_serial_port=è¯·éæ©ä¸²å£ error=é误 open_serial_first=è¯·å æå¼ä¸²å£ input_data_to_send=请è¾å ¥è¦åéçæ°æ® send_failed=åé失败 receive=æ¥æ¶ visualization=å¯è§å config_read_success=é 置读åæå info=æç¤º input_valid_number=请è¾å ¥ææçæ°å config_save_success=é ç½®ä¿åæå select_file=éæ©æä»¶ upgrade=å级 upgrade_progress=å级è¿åº¦ select_bin_file=è¯·éæ©binæä»¶ upgrade_success=å级æå upgrade_failed=å级失败 hex=HEX ascii=ASCII hex_send=HEXåé close_serial=å ³éä¸²å£ systemfile/logfile/openlog.txt
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,382 @@ ç¨åºå¯å¨: 2025-08-20 22:25:39 ç¨åºå ³é: 2025-08-20 22:27:05 工使¶é¿: 0å°æ¶ 1åé 26ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-20 22:27:08 ç¨åºå ³é: 2025-08-20 22:27:10 工使¶é¿: 0å°æ¶ 0åé 1ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-21 22:06:32 ç¨åºå¯å¨: 2025-08-21 22:22:27 ç¨åºå ³é: 2025-08-21 22:22:31 工使¶é¿: 0å°æ¶ 15åé 59ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-21 22:22:39 ç¨åºå ³é: 2025-08-21 22:23:26 工使¶é¿: 0å°æ¶ 0åé 46ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-21 22:33:26 ç¨åºå ³é: 2025-08-21 22:34:02 工使¶é¿: 0å°æ¶ 0åé 35ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-21 22:52:48 ç¨åºå ³é: 2025-08-21 22:53:09 工使¶é¿: 0å°æ¶ 0åé 21ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-21 23:02:51 ç¨åºå ³é: 2025-08-21 23:03:18 工使¶é¿: 0å°æ¶ 0åé 26ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-21 23:03:48 ç¨åºå ³é: 2025-08-21 23:04:58 工使¶é¿: 0å°æ¶ 1åé 10ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-21 23:07:06 ç¨åºå ³é: 2025-08-21 23:10:42 工使¶é¿: 0å°æ¶ 3åé 35ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-21 23:10:43 ç¨åºå ³é: 2025-08-21 23:16:26 工使¶é¿: 0å°æ¶ 5åé 42ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-21 23:16:30 ç¨åºå ³é: 2025-08-21 23:16:53 工使¶é¿: 0å°æ¶ 0åé 23ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 09:40:59 ç¨åºå ³é: 2025-08-23 10:18:56 工使¶é¿: 0å°æ¶ 37åé 56ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 10:19:01 ç¨åºå¯å¨: 2025-08-23 10:32:55 ç¨åºå ³é: 2025-08-23 10:32:59 工使¶é¿: 0å°æ¶ 13åé 58ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 10:33:01 ç¨åºå¯å¨: 2025-08-23 10:40:28 ç¨åºå ³é: 2025-08-23 10:40:31 工使¶é¿: 0å°æ¶ 7åé 30ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 10:40:33 ç¨åºå ³é: 2025-08-23 10:40:40 工使¶é¿: 0å°æ¶ 0åé 7ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 10:44:02 ç¨åºå ³é: 2025-08-23 10:44:10 工使¶é¿: 0å°æ¶ 0åé 8ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 11:02:49 ç¨åºå ³é: 2025-08-23 11:02:50 工使¶é¿: 0å°æ¶ 0åé 1ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 11:03:13 ç¨åºå ³é: 2025-08-23 11:03:15 工使¶é¿: 0å°æ¶ 0åé 1ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 11:08:59 ç¨åºå ³é: 2025-08-23 11:20:47 工使¶é¿: 0å°æ¶ 11åé 48ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 11:20:49 ç¨åºå ³é: 2025-08-23 11:45:08 工使¶é¿: 0å°æ¶ 24åé 19ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 11:45:10 ç¨åºå ³é: 2025-08-23 11:45:11 工使¶é¿: 0å°æ¶ 0åé 1ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 11:46:25 ç¨åºå ³é: 2025-08-23 11:52:37 工使¶é¿: 0å°æ¶ 6åé 11ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 11:52:38 ç¨åºå¯å¨: 2025-08-23 11:53:48 ç¨åºå ³é: 2025-08-23 11:53:51 工使¶é¿: 0å°æ¶ 1åé 12ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 11:53:53 ç¨åºå ³é: 2025-08-23 12:20:32 工使¶é¿: 0å°æ¶ 26åé 39ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 12:20:34 ç¨åºå ³é: 2025-08-23 12:21:04 工使¶é¿: 0å°æ¶ 0åé 30ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 12:24:07 ç¨åºå ³é: 2025-08-23 12:32:02 工使¶é¿: 0å°æ¶ 7åé 54ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 12:32:05 ç¨åºå ³é: 2025-08-23 12:32:20 工使¶é¿: 0å°æ¶ 0åé 15ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 12:32:52 ç¨åºå¯å¨: 2025-08-23 12:34:29 ç¨åºå ³é: 2025-08-23 12:34:46 工使¶é¿: 0å°æ¶ 0åé 16ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 12:35:10 ç¨åºå¯å¨: 2025-08-23 12:36:54 ç¨åºå ³é: 2025-08-23 12:36:55 工使¶é¿: 0å°æ¶ 0åé 1ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 12:37:21 ç¨åºå ³é: 2025-08-23 12:50:13 工使¶é¿: 0å°æ¶ 12åé 52ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 12:50:15 ç¨åºå ³é: 2025-08-23 12:55:28 工使¶é¿: 0å°æ¶ 5åé 12ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 12:55:30 ç¨åºå ³é: 2025-08-23 12:55:45 工使¶é¿: 0å°æ¶ 0åé 15ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 12:59:36 ç¨åºå¯å¨: 2025-08-23 12:59:46 ç¨åºå¯å¨: 2025-08-23 12:59:50 ç¨åºå¯å¨: 2025-08-23 13:00:14 ç¨åºå ³é: 2025-08-23 13:00:31 工使¶é¿: 0å°æ¶ 0åé 16ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 13:01:24 ç¨åºå¯å¨: 2025-08-23 13:07:11 ç¨åºå ³é: 2025-08-23 13:07:14 工使¶é¿: 0å°æ¶ 5åé 50ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 13:07:16 ç¨åºå ³é: 2025-08-23 13:09:02 工使¶é¿: 0å°æ¶ 1åé 45ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 13:11:28 ç¨åºå ³é: 2025-08-23 13:11:50 工使¶é¿: 0å°æ¶ 0åé 22ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 13:15:40 ç¨åºå ³é: 2025-08-23 13:16:03 工使¶é¿: 0å°æ¶ 0åé 23ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 13:18:57 ç¨åºå¯å¨: 2025-08-23 13:26:38 ç¨åºå ³é: 2025-08-23 13:27:34 工使¶é¿: 0å°æ¶ 0åé 56ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 13:30:01 ç¨åºå ³é: 2025-08-23 13:43:02 工使¶é¿: 0å°æ¶ 13åé 1ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 13:47:21 ç¨åºå ³é: 2025-08-23 13:47:27 工使¶é¿: 0å°æ¶ 0åé 6ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 13:49:29 ç¨åºå ³é: 2025-08-23 13:55:16 工使¶é¿: 0å°æ¶ 5åé 47ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 13:55:18 ç¨åºå ³é: 2025-08-23 13:55:42 工使¶é¿: 0å°æ¶ 0åé 23ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 13:57:28 ç¨åºå ³é: 2025-08-23 14:00:15 工使¶é¿: 0å°æ¶ 2åé 47ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:04:46 ç¨åºå ³é: 2025-08-23 14:06:11 工使¶é¿: 0å°æ¶ 1åé 24ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:10:00 ç¨åºå ³é: 2025-08-23 14:10:50 工使¶é¿: 0å°æ¶ 0åé 50ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:11:46 ç¨åºå ³é: 2025-08-23 14:12:00 工使¶é¿: 0å°æ¶ 0åé 14ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:12:45 ç¨åºå ³é: 2025-08-23 14:14:12 工使¶é¿: 0å°æ¶ 1åé 27ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:15:18 ç¨åºå ³é: 2025-08-23 14:15:41 工使¶é¿: 0å°æ¶ 0åé 23ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:16:02 ç¨åºå ³é: 2025-08-23 14:16:33 工使¶é¿: 0å°æ¶ 0åé 30ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:16:45 ç¨åºå ³é: 2025-08-23 14:20:49 工使¶é¿: 0å°æ¶ 4åé 4ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:20:52 ç¨åºå ³é: 2025-08-23 14:22:58 工使¶é¿: 0å°æ¶ 2åé 6ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:24:55 ç¨åºå ³é: 2025-08-23 14:36:39 工使¶é¿: 0å°æ¶ 11åé 43ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:36:41 ç¨åºå ³é: 2025-08-23 14:51:47 工使¶é¿: 0å°æ¶ 15åé 5ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:51:48 ç¨åºå ³é: 2025-08-23 14:52:27 工使¶é¿: 0å°æ¶ 0åé 38ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:53:43 ç¨åºå ³é: 2025-08-23 14:56:54 工使¶é¿: 0å°æ¶ 3åé 10ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 14:56:56 ç¨åºå ³é: 2025-08-23 14:58:50 工使¶é¿: 0å°æ¶ 1åé 54ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 15:01:18 ç¨åºå ³é: 2025-08-23 15:01:46 工使¶é¿: 0å°æ¶ 0åé 27ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 15:02:42 ç¨åºå ³é: 2025-08-23 15:02:55 工使¶é¿: 0å°æ¶ 0åé 13ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 15:07:22 ç¨åºå ³é: 2025-08-23 15:07:43 工使¶é¿: 0å°æ¶ 0åé 21ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 15:09:08 ç¨åºå ³é: 2025-08-23 15:09:20 工使¶é¿: 0å°æ¶ 0åé 11ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 15:13:00 ç¨åºå ³é: 2025-08-23 15:13:14 工使¶é¿: 0å°æ¶ 0åé 14ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 15:16:58 ç¨åºå ³é: 2025-08-23 15:17:26 工使¶é¿: 0å°æ¶ 0åé 28ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-23 15:21:38 ç¨åºå ³é: 2025-08-23 15:30:58 工使¶é¿: 0å°æ¶ 9åé 20ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-24 22:40:40 ç¨åºå ³é: 2025-08-24 22:40:51 工使¶é¿: 0å°æ¶ 0åé 11ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 14:33:34 ç¨åºå¯å¨: 2025-08-25 15:38:47 ç¨åºå¯å¨: 2025-08-25 16:02:07 ç¨åºå¯å¨: 2025-08-25 16:08:36 ç¨åºå ³é: 2025-08-25 16:20:36 工使¶é¿: 0å°æ¶ 11åé 59ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 16:20:38 ç¨åºå ³é: 2025-08-25 16:20:54 工使¶é¿: 0å°æ¶ 0åé 16ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 16:21:39 ç¨åºå ³é: 2025-08-25 16:21:48 工使¶é¿: 0å°æ¶ 0åé 8ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 16:26:43 ç¨åºå ³é: 2025-08-25 16:27:15 工使¶é¿: 0å°æ¶ 0åé 31ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 16:36:31 ç¨åºå ³é: 2025-08-25 16:44:31 工使¶é¿: 0å°æ¶ 7åé 59ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 21:47:03 ç¨åºå ³é: 2025-08-25 21:47:25 工使¶é¿: 0å°æ¶ 0åé 22ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 21:47:39 ç¨åºå ³é: 2025-08-25 21:48:51 工使¶é¿: 0å°æ¶ 1åé 11ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 21:49:22 ç¨åºå ³é: 2025-08-25 21:52:09 工使¶é¿: 0å°æ¶ 2åé 46ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 21:52:10 ç¨åºå ³é: 2025-08-25 21:53:22 工使¶é¿: 0å°æ¶ 1åé 11ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 21:53:28 ç¨åºå ³é: 2025-08-25 21:53:46 工使¶é¿: 0å°æ¶ 0åé 17ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 21:54:00 ç¨åºå ³é: 2025-08-25 22:13:45 工使¶é¿: 0å°æ¶ 19åé 44ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 22:14:44 ç¨åºå ³é: 2025-08-25 22:14:56 工使¶é¿: 0å°æ¶ 0åé 12ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 22:15:26 ç¨åºå ³é: 2025-08-25 22:15:54 工使¶é¿: 0å°æ¶ 0åé 27ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 22:15:58 ç¨åºå ³é: 2025-08-25 22:16:19 工使¶é¿: 0å°æ¶ 0åé 20ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 22:18:58 ç¨åºå¯å¨: 2025-08-25 22:24:19 ç¨åºå ³é: 2025-08-25 22:27:56 工使¶é¿: 0å°æ¶ 8åé 58ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 22:27:58 ç¨åºå¯å¨: 2025-08-25 22:31:32 ç¨åºå ³é: 2025-08-25 22:31:42 工使¶é¿: 0å°æ¶ 0åé 9ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 22:33:09 ç¨åºå ³é: 2025-08-25 22:33:36 工使¶é¿: 0å°æ¶ 0åé 26ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 22:35:48 ç¨åºå¯å¨: 2025-08-25 22:36:24 ç¨åºå ³é: 2025-08-25 22:36:28 工使¶é¿: 0å°æ¶ 0åé 40ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 22:36:30 ç¨åºå ³é: 2025-08-25 22:37:35 工使¶é¿: 0å°æ¶ 1åé 5ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 22:48:24 ç¨åºå ³é: 2025-08-25 22:59:07 工使¶é¿: 0å°æ¶ 10åé 43ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 23:04:12 ç¨åºå ³é: 2025-08-25 23:04:23 工使¶é¿: 0å°æ¶ 0åé 11ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-25 23:04:36 ç¨åºå ³é: 2025-08-25 23:04:44 工使¶é¿: 0å°æ¶ 0åé 8ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-26 14:02:07 ç¨åºå ³é: 2025-08-26 14:03:39 工使¶é¿: 0å°æ¶ 1åé 31ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-26 14:08:31 ç¨åºå ³é: 2025-08-26 14:08:42 工使¶é¿: 0å°æ¶ 0åé 10ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-26 14:15:00 ç¨åºå ³é: 2025-08-26 14:16:29 工使¶é¿: 0å°æ¶ 1åé 29ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-26 16:01:22 ç¨åºå ³é: 2025-08-26 16:02:35 工使¶é¿: 0å°æ¶ 1åé 13ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-26 16:02:37 ç¨åºå ³é: 2025-08-26 17:12:18 工使¶é¿: 1å°æ¶ 9åé 40ç§ ----------------------------------- ç¨åºå¯å¨: 2025-08-26 17:12:20