package gecaoji; import javax.swing.*; import publicway.buttonset; 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 pathIdLabel; private JLabel batteryLevelLabel; private JLabel batteryVoltageLabel; private JLabel operationModeLabel; private JLabel motorStatusLabel; private JLabel bladeStatusLabel; private JLabel bladeHeightLabel; private JLabel selfCheckStatusLabel; private JLabel errorCodeLabel; private JLabel errorMessageLabel; private JLabel rollLabel; private JLabel pitchLabel; private JLabel yawLabel; 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); // 新增属性显示 int currentRow = 7; // 存储的路径ID addLabelRow(grid, gbc, currentRow++, "路径ID:", pathIdLabel = createValueLabel(), titleFont, valueFont); // 电池电量 addLabelRow(grid, gbc, currentRow++, "电池电量:", batteryLevelLabel = createValueLabel(), titleFont, valueFont); // 电池电压 addLabelRow(grid, gbc, currentRow++, "电池电压:", batteryVoltageLabel = createValueLabel(), titleFont, valueFont); // 操作模式 addLabelRow(grid, gbc, currentRow++, "操作模式:", operationModeLabel = createValueLabel(), titleFont, valueFont); // 电机状态 addLabelRow(grid, gbc, currentRow++, "电机状态:", motorStatusLabel = createValueLabel(), titleFont, valueFont); // 刀片状态 addLabelRow(grid, gbc, currentRow++, "刀片状态:", bladeStatusLabel = createValueLabel(), titleFont, valueFont); // 刀盘高度 addLabelRow(grid, gbc, currentRow++, "刀盘高度:", bladeHeightLabel = createValueLabel(), titleFont, valueFont); // 自检状态 addLabelRow(grid, gbc, currentRow++, "自检状态:", selfCheckStatusLabel = createValueLabel(), titleFont, valueFont); // 错误代码 addLabelRow(grid, gbc, currentRow++, "错误代码:", errorCodeLabel = createValueLabel(), titleFont, valueFont); // 错误信息 addLabelRow(grid, gbc, currentRow++, "错误信息:", errorMessageLabel = createValueLabel(), titleFont, valueFont); // 横滚角 addLabelRow(grid, gbc, currentRow++, "横滚角:", rollLabel = createValueLabel(), titleFont, valueFont); // 俯仰角 addLabelRow(grid, gbc, currentRow++, "俯仰角:", pitchLabel = createValueLabel(), titleFont, valueFont); // 偏航角 addLabelRow(grid, gbc, currentRow++, "偏航角:", yawLabel = createValueLabel(), titleFont, valueFont); // 更新时间 gbc.gridx = 0; gbc.gridy = currentRow; 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 = currentRow; gbc.weightx = 0.7; updateTimeLabel = createValueLabel(); updateTimeLabel.setFont(valueFont); grid.add(updateTimeLabel, gbc); content.add(grid, BorderLayout.CENTER); // 按钮面板美化 JButton closeButton = buttonset.createStyledButton("关闭", new Color(0, 102, 204)); closeButton.setFont(new Font("Microsoft YaHei", Font.BOLD, 13)); 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; pathIdLabel = null; batteryLevelLabel = null; batteryVoltageLabel = null; operationModeLabel = null; motorStatusLabel = null; bladeStatusLabel = null; bladeHeightLabel = null; selfCheckStatusLabel = null; errorCodeLabel = null; errorMessageLabel = null; rollLabel = null; pitchLabel = null; yawLabel = 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())); // 更新新增属性 pathIdLabel.setText(formatDeviceValue(device.getPath_id_saved())); batteryLevelLabel.setText(formatDeviceValue(device.getBattery_level())); batteryVoltageLabel.setText(formatDeviceValue(device.getBattery_voltage())); operationModeLabel.setText(formatDeviceValue(device.getOperation_mode())); motorStatusLabel.setText(formatDeviceValue(device.getMotor_status())); bladeStatusLabel.setText(formatDeviceValue(device.getBlade_status())); bladeHeightLabel.setText(formatDeviceValue(device.getBlade_height())); selfCheckStatusLabel.setText(formatDeviceValue(device.getSelf_check_status())); errorCodeLabel.setText(formatDeviceValue(device.getError_code())); errorMessageLabel.setText(formatDeviceValue(device.getError_message())); rollLabel.setText(formatDeviceValue(device.getRoll())); pitchLabel.setText(formatDeviceValue(device.getPitch())); yawLabel.setText(formatDeviceValue(device.getYaw())); 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 (pathIdLabel != null) pathIdLabel.setText(value); if (batteryLevelLabel != null) batteryLevelLabel.setText(value); if (batteryVoltageLabel != null) batteryVoltageLabel.setText(value); if (operationModeLabel != null) operationModeLabel.setText(value); if (motorStatusLabel != null) motorStatusLabel.setText(value); if (bladeStatusLabel != null) bladeStatusLabel.setText(value); if (bladeHeightLabel != null) bladeHeightLabel.setText(value); if (selfCheckStatusLabel != null) selfCheckStatusLabel.setText(value); if (errorCodeLabel != null) errorCodeLabel.setText(value); if (errorMessageLabel != null) errorMessageLabel.setText(value); if (rollLabel != null) rollLabel.setText(value); if (pitchLabel != null) pitchLabel.setText(value); if (yawLabel != null) yawLabel.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; } } private void addLabelRow(JPanel grid, GridBagConstraints gbc, int row, String title, JLabel valueLabel, Font titleFont, Font valueFont) { gbc.gridx = 0; gbc.gridy = row; gbc.weightx = 0.3; JLabel label = new JLabel(title); label.setFont(titleFont); label.setForeground(new Color(102, 102, 102)); grid.add(label, gbc); gbc.gridx = 1; gbc.gridy = row; gbc.weightx = 0.7; valueLabel.setFont(valueFont); grid.add(valueLabel, gbc); } }