package gecaoji; import javax.swing.*; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.text.SimpleDateFormat; import java.util.Date; // Manages the mower info dialog to keep MapRenderer focused on rendering. public class GecaojiMeg { private static final SimpleDateFormat TIMESTAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private final JPanel anchorPanel; private final Gecaoji mower; private JDialog infoDialog; private Timer refreshTimer; private JLabel mowerNumberLabel; private JLabel realtimeXLabel; private JLabel realtimeYLabel; private JLabel positioningStatusLabel; private JLabel satelliteCountLabel; private JLabel realtimeSpeedLabel; private JLabel headingLabel; private JLabel updateTimeLabel; public GecaojiMeg(JPanel anchorPanel, Gecaoji mower) { this.anchorPanel = anchorPanel; this.mower = mower; } public void showMowerInfo() { Device device = Device.getGecaoji(); if (device == null) { JOptionPane.showMessageDialog( anchorPanel, "无设备数据", "割草机信息", JOptionPane.INFORMATION_MESSAGE ); return; } ensureDialog(); updateLabels(); positionDialog(); if (!infoDialog.isVisible()) { infoDialog.setVisible(true); } else { infoDialog.toFront(); } startTimer(); } public void dispose() { stopTimer(); if (infoDialog != null) { infoDialog.dispose(); infoDialog = null; } resetLabels(); } private void ensureDialog() { if (infoDialog != null) { return; } Window owner = anchorPanel == null ? null : SwingUtilities.getWindowAncestor(anchorPanel); JDialog dialog = new JDialog(owner, "割草机信息", Dialog.ModalityType.MODELESS); dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); // 设置对话框字体和颜色 dialog.getContentPane().setBackground(new Color(245, 245, 245)); JPanel content = new JPanel(new BorderLayout(0, 8)); // 减小垂直间距为8 content.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); // 减小内边距 content.setBackground(new Color(245, 245, 245)); // 创建信息面板 - 使用GridBagLayout以获得更精细的控制 JPanel grid = new JPanel(new GridBagLayout()); grid.setBackground(new Color(255, 255, 255)); grid.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(new Color(220, 220, 220), 1), BorderFactory.createEmptyBorder(10, 12, 10, 12) // 减小内边距 )); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(5, 8, 5, 8); // 单元格间距缩小为原来的一半 gbc.fill = GridBagConstraints.HORIZONTAL; gbc.anchor = GridBagConstraints.WEST; // 设置标题标签样式 Font titleFont = new Font("Microsoft YaHei", Font.BOLD, 13); Font valueFont = new Font("Microsoft YaHei", Font.PLAIN, 13); // 第一行 gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 0.3; JLabel label1 = new JLabel("设备编号:"); label1.setFont(titleFont); label1.setForeground(new Color(102, 102, 102)); grid.add(label1, gbc); gbc.gridx = 1; gbc.gridy = 0; gbc.weightx = 0.7; mowerNumberLabel = createValueLabel(); mowerNumberLabel.setFont(valueFont); grid.add(mowerNumberLabel, gbc); // 第二行 gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 0.3; JLabel label2 = new JLabel("实时X坐标:"); label2.setFont(titleFont); label2.setForeground(new Color(102, 102, 102)); grid.add(label2, gbc); gbc.gridx = 1; gbc.gridy = 1; gbc.weightx = 0.7; realtimeXLabel = createValueLabel(); realtimeXLabel.setFont(valueFont); grid.add(realtimeXLabel, gbc); // 第三行 gbc.gridx = 0; gbc.gridy = 2; gbc.weightx = 0.3; JLabel label3 = new JLabel("实时Y坐标:"); label3.setFont(titleFont); label3.setForeground(new Color(102, 102, 102)); grid.add(label3, gbc); gbc.gridx = 1; gbc.gridy = 2; gbc.weightx = 0.7; realtimeYLabel = createValueLabel(); realtimeYLabel.setFont(valueFont); grid.add(realtimeYLabel, gbc); // 第四行 gbc.gridx = 0; gbc.gridy = 3; gbc.weightx = 0.3; JLabel label4 = new JLabel("定位质量:"); label4.setFont(titleFont); label4.setForeground(new Color(102, 102, 102)); grid.add(label4, gbc); gbc.gridx = 1; gbc.gridy = 3; gbc.weightx = 0.7; positioningStatusLabel = createValueLabel(); positioningStatusLabel.setFont(valueFont); grid.add(positioningStatusLabel, gbc); // 第五行 gbc.gridx = 0; gbc.gridy = 4; gbc.weightx = 0.3; JLabel label5 = new JLabel("卫星颗数:"); label5.setFont(titleFont); label5.setForeground(new Color(102, 102, 102)); grid.add(label5, gbc); gbc.gridx = 1; gbc.gridy = 4; gbc.weightx = 0.7; satelliteCountLabel = createValueLabel(); satelliteCountLabel.setFont(valueFont); grid.add(satelliteCountLabel, gbc); // 第六行 gbc.gridx = 0; gbc.gridy = 5; gbc.weightx = 0.3; JLabel label6 = new JLabel("行驶速度:"); label6.setFont(titleFont); label6.setForeground(new Color(102, 102, 102)); grid.add(label6, gbc); gbc.gridx = 1; gbc.gridy = 5; gbc.weightx = 0.7; realtimeSpeedLabel = createValueLabel(); realtimeSpeedLabel.setFont(valueFont); grid.add(realtimeSpeedLabel, gbc); // 第七行 gbc.gridx = 0; gbc.gridy = 6; gbc.weightx = 0.3; JLabel label7 = new JLabel("运动航向:"); label7.setFont(titleFont); label7.setForeground(new Color(102, 102, 102)); grid.add(label7, gbc); gbc.gridx = 1; gbc.gridy = 6; gbc.weightx = 0.7; headingLabel = createValueLabel(); headingLabel.setFont(valueFont); grid.add(headingLabel, gbc); // 第八行 gbc.gridx = 0; gbc.gridy = 7; gbc.weightx = 0.3; JLabel label8 = new JLabel("更新时间:"); label8.setFont(titleFont); label8.setForeground(new Color(102, 102, 102)); grid.add(label8, gbc); gbc.gridx = 1; gbc.gridy = 7; gbc.weightx = 0.7; updateTimeLabel = createValueLabel(); updateTimeLabel.setFont(valueFont); grid.add(updateTimeLabel, gbc); content.add(grid, BorderLayout.CENTER); // 按钮面板美化 JButton closeButton = new JButton("关闭"); closeButton.setFont(new Font("Microsoft YaHei", Font.BOLD, 13)); closeButton.setBackground(new Color(0, 102, 204)); closeButton.setForeground(Color.WHITE); closeButton.setFocusPainted(false); closeButton.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(new Color(0, 80, 180), 1), BorderFactory.createEmptyBorder(5, 18, 5, 18) // 减小按钮内边距 )); closeButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); // 鼠标悬停效果 closeButton.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseEntered(java.awt.event.MouseEvent evt) { closeButton.setBackground(new Color(0, 122, 255)); } public void mouseExited(java.awt.event.MouseEvent evt) { closeButton.setBackground(new Color(0, 102, 204)); } }); closeButton.addActionListener(e -> dialog.dispose()); JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); buttonPanel.setBackground(new Color(245, 245, 245)); buttonPanel.add(closeButton); content.add(buttonPanel, BorderLayout.SOUTH); dialog.setContentPane(content); dialog.pack(); dialog.setResizable(false); dialog.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { stopTimer(); resetLabels(); infoDialog = null; } }); infoDialog = dialog; } private void positionDialog() { if (infoDialog == null || anchorPanel == null) { return; } int panelWidth = anchorPanel.getWidth(); int panelHeight = anchorPanel.getHeight(); int dialogHeight = infoDialog.getHeight(); if (dialogHeight <= 0) { dialogHeight = infoDialog.getPreferredSize().height; } if (panelWidth > 0) { infoDialog.setSize(panelWidth, dialogHeight); } try { Point panelOnScreen = anchorPanel.getLocationOnScreen(); int dialogWidth = infoDialog.getWidth(); int targetX = panelOnScreen.x + (panelWidth - dialogWidth) / 2; int targetY = panelOnScreen.y + panelHeight / 3 - dialogHeight / 2; infoDialog.setLocation(targetX, targetY); } catch (IllegalComponentStateException ignored) { // panel not yet visible, skip positioning for now } } private JLabel createValueLabel() { JLabel label = new JLabel("--"); label.setHorizontalAlignment(SwingConstants.LEFT); label.setForeground(new Color(51, 51, 51)); return label; } private void startTimer() { if (refreshTimer == null) { refreshTimer = new Timer(1000, e -> updateLabels()); refreshTimer.setRepeats(true); } if (!refreshTimer.isRunning()) { refreshTimer.start(); } } private void stopTimer() { if (refreshTimer != null) { refreshTimer.stop(); refreshTimer = null; } } private void resetLabels() { mowerNumberLabel = null; realtimeXLabel = null; realtimeYLabel = null; positioningStatusLabel = null; satelliteCountLabel = null; realtimeSpeedLabel = null; headingLabel = null; updateTimeLabel = null; } private void updateLabels() { if (mowerNumberLabel == null) { return; } Device device = Device.getGecaoji(); if (device == null) { setLabels("--"); return; } mower.refreshFromDevice(); mowerNumberLabel.setText(formatDeviceValue(device.getMowerNumber())); realtimeXLabel.setText(formatDeviceValue(device.getRealtimeX())); realtimeYLabel.setText(formatDeviceValue(device.getRealtimeY())); positioningStatusLabel.setText(formatFixQualityValue(device.getPositioningStatus())); satelliteCountLabel.setText(formatDeviceValue(device.getSatelliteCount())); realtimeSpeedLabel.setText(formatDeviceValue(device.getRealtimeSpeed())); headingLabel.setText(formatDeviceValue(device.getHeading())); updateTimeLabel.setText(formatTimestamp(device.getGupdateTime())); } private void setLabels(String value) { if (mowerNumberLabel != null) mowerNumberLabel.setText(value); if (realtimeXLabel != null) realtimeXLabel.setText(value); if (realtimeYLabel != null) realtimeYLabel.setText(value); if (positioningStatusLabel != null) positioningStatusLabel.setText(value); if (satelliteCountLabel != null) satelliteCountLabel.setText(value); if (realtimeSpeedLabel != null) realtimeSpeedLabel.setText(value); if (headingLabel != null) headingLabel.setText(value); if (updateTimeLabel != null) updateTimeLabel.setText(value); } private String sanitizeDeviceValue(String value) { if (value == null) { return null; } String trimmed = value.trim(); if (trimmed.isEmpty() || "-1".equals(trimmed)) { return null; } return trimmed; } private String formatDeviceValue(String value) { String sanitized = sanitizeDeviceValue(value); return sanitized == null ? "--" : sanitized; } private String formatFixQualityValue(String value) { String sanitized = sanitizeDeviceValue(value); if (sanitized == null) { return "--"; } switch (sanitized) { case "0": return "未定位"; case "1": return "单点定位"; case "2": return "码差分"; case "3": return "无效PPS"; case "4": return "固定解"; case "5": return "浮点解"; case "6": return "正在估算"; case "7": return "人工输入固定值"; case "8": return "模拟模式"; case "9": return "WAAS差分"; default: return sanitized; } } private String formatTimestamp(String value) { String sanitized = sanitizeDeviceValue(value); if (sanitized == null) { return "--"; } try { long millis = Long.parseLong(sanitized); return TIMESTAMP_FORMAT.format(new Date(millis)); } catch (NumberFormatException ex) { return sanitized; } } }