| | |
| | | package set; |
| | | |
| | | import baseStation.BaseStation; |
| | | import gecaoji.Device; |
| | | import gecaoji.MowerSafetyDistanceCalculator; |
| | | |
| | | import zhuye.MapRenderer; |
| | | import zhuye.Shouye; |
| | | import zhuye.buttonset; |
| | | import set.Setsys; |
| | | |
| | | import zhuye.celiangmoshi; |
| | | import javax.swing.*; |
| | | import javax.swing.filechooser.FileNameExtensionFilter; |
| | | |
| | | import java.awt.*; |
| | | import java.awt.event.*; |
| | | import java.awt.geom.RoundRectangle2D; |
| | | import java.io.File; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | |
| | | |
| | | // 设置项组件 |
| | | private JLabel mowerIdLabel; |
| | | private JLabel mowerSizeLabel; |
| | | private JLabel mowingSafetyDistanceLabel; |
| | | private JLabel baseStationIdLabel; |
| | | private JLabel handheldMarkerLabel; |
| | | private JLabel simCardNumberLabel; |
| | |
| | | private JLabel appVersionLabel; |
| | | private JLabel idleTrailDurationLabel; |
| | | private JLabel boundaryLengthVisibleLabel; |
| | | private JLabel measurementModeEnabledLabel; |
| | | |
| | | private JButton mowerIdEditBtn; |
| | | private JButton mowerSizeEditBtn; |
| | | private JButton mowingSafetyDistanceEditBtn; |
| | | private JButton baseStationIdEditBtn; |
| | | private JButton handheldEditBtn; |
| | | private JButton checkUpdateBtn; |
| | |
| | | mowerIdLabel = (JLabel) mowerIdPanel.getClientProperty("valueLabel"); |
| | | mowerIdEditBtn = (JButton) mowerIdPanel.getClientProperty("editButton"); |
| | | |
| | | // 割草机长宽 |
| | | JPanel mowerSizePanel = createSettingItemPanel("割草机长宽", |
| | | formatMowerSize(), true); |
| | | mowerSizeLabel = (JLabel) mowerSizePanel.getClientProperty("valueLabel"); |
| | | mowerSizeEditBtn = (JButton) mowerSizePanel.getClientProperty("editButton"); |
| | | |
| | | // 割草安全距离 |
| | | JPanel mowingSafetyDistancePanel = createSettingItemPanel("割草安全距离", |
| | | formatMowingSafetyDistance(), true); |
| | | mowingSafetyDistanceLabel = (JLabel) mowingSafetyDistancePanel.getClientProperty("valueLabel"); |
| | | mowingSafetyDistanceEditBtn = (JButton) mowingSafetyDistancePanel.getClientProperty("editButton"); |
| | | |
| | | JPanel baseStationIdPanel = createSettingItemPanel("差分基准站编号", |
| | | resolveBaseStationId(), true); |
| | | baseStationIdLabel = (JLabel) baseStationIdPanel.getClientProperty("valueLabel"); |
| | |
| | | // 显示边界距离设置项 |
| | | JPanel boundaryLengthPanel = createBoundaryLengthPanel(); |
| | | boundaryLengthVisibleLabel = (JLabel) boundaryLengthPanel.getClientProperty("valueLabel"); |
| | | |
| | | // 开启测量模式设置项 |
| | | JPanel measurementModePanel = createMeasurementModePanel(); |
| | | measurementModeEnabledLabel = (JLabel) measurementModePanel.getClientProperty("valueLabel"); |
| | | |
| | | JPanel feedbackPanel = createFeedbackPanel(); |
| | | |
| | |
| | | |
| | | // 添加设置项,使用分割线分隔 |
| | | addSettingItem(panel, mowerIdPanel, true); |
| | | addSettingItem(panel, mowerSizePanel, true); |
| | | addSettingItem(panel, mowingSafetyDistancePanel, true); |
| | | addSettingItem(panel, baseStationIdPanel, true); |
| | | addSettingItem(panel, handheldPanel, true); |
| | | addSettingItem(panel, simCardPanel, true); |
| | |
| | | addSettingItem(panel, firmwarePanel, true); |
| | | addSettingItem(panel, idleTrailPanel, true); |
| | | addSettingItem(panel, boundaryLengthPanel, true); |
| | | addSettingItem(panel, measurementModePanel, true); |
| | | addSettingItem(panel, feedbackPanel, true); |
| | | addSettingItem(panel, appVersionPanel, false); // 最后一项不加分割线 |
| | | |
| | |
| | | return seconds + "秒"; |
| | | } |
| | | |
| | | /** |
| | | * 将字符串值转换为米(兼容旧数据,如果值大于100认为是厘米,需要除以100) |
| | | */ |
| | | private double convertToMeters(String value) { |
| | | if (value == null || value.trim().isEmpty() || "-1".equals(value.trim())) { |
| | | return -1; |
| | | } |
| | | try { |
| | | double val = Double.parseDouble(value.trim()); |
| | | // 如果值大于100,认为是厘米,需要转换为米 |
| | | if (val > 100) { |
| | | return val / 100.0; |
| | | } |
| | | return val; |
| | | } catch (NumberFormatException e) { |
| | | return -1; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 格式化数值为米,保留2位小数 |
| | | */ |
| | | private String formatMeters(double value) { |
| | | if (value < 0) { |
| | | return "未设置"; |
| | | } |
| | | return String.format("%.2f", value) + "m"; |
| | | } |
| | | |
| | | private String formatMowerSize() { |
| | | Device device = Device.getActiveDevice(); |
| | | if (device == null) { |
| | | return "未设置"; |
| | | } |
| | | String width = device.getMowerWidth(); |
| | | String length = device.getMowerLength(); |
| | | double widthMeters = convertToMeters(width); |
| | | double lengthMeters = convertToMeters(length); |
| | | |
| | | if (widthMeters < 0 && lengthMeters < 0) { |
| | | return "未设置"; |
| | | } |
| | | |
| | | String widthStr = widthMeters >= 0 ? formatMeters(widthMeters) : "未设置"; |
| | | String lengthStr = lengthMeters >= 0 ? formatMeters(lengthMeters) : "未设置"; |
| | | return lengthStr + " × " + widthStr; |
| | | } |
| | | |
| | | private String formatMowingSafetyDistance() { |
| | | Device device = Device.getActiveDevice(); |
| | | if (device == null) { |
| | | return "未设置"; |
| | | } |
| | | String distance = device.getMowingSafetyDistance(); |
| | | double distanceMeters = convertToMeters(distance); |
| | | if (distanceMeters < 0) { |
| | | return "未设置"; |
| | | } |
| | | return formatMeters(distanceMeters); |
| | | } |
| | | |
| | | |
| | | private JPanel createSettingItemPanel(String title, String value, boolean editable) { |
| | | JPanel panel = new JPanel(new GridBagLayout()); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 创建开启测量模式设置面板 |
| | | */ |
| | | private JPanel createMeasurementModePanel() { |
| | | JPanel panel = new JPanel(new GridBagLayout()); |
| | | panel.setOpaque(false); // 透明背景 |
| | | panel.setAlignmentX(Component.LEFT_ALIGNMENT); |
| | | panel.setMaximumSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT)); |
| | | panel.setPreferredSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT)); |
| | | panel.setMinimumSize(new Dimension(0, ROW_HEIGHT)); |
| | | panel.setBorder(BorderFactory.createEmptyBorder(ITEM_PADDING, ITEM_PADDING, ITEM_PADDING, ITEM_PADDING)); |
| | | |
| | | GridBagConstraints gbc = new GridBagConstraints(); |
| | | |
| | | JLabel titleLabel = new JLabel("开启测量模式"); |
| | | titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); |
| | | titleLabel.setForeground(Color.BLACK); |
| | | titleLabel.setHorizontalAlignment(SwingConstants.RIGHT); |
| | | gbc.gridx = 0; |
| | | gbc.gridy = 0; |
| | | gbc.weightx = 0; |
| | | gbc.anchor = GridBagConstraints.EAST; |
| | | gbc.insets = new Insets(0, 0, 0, 12); |
| | | panel.add(titleLabel, gbc); |
| | | |
| | | measurementModeEnabledLabel = new JLabel(setData.isMeasurementModeEnabled() ? "已开启" : "已关闭"); |
| | | measurementModeEnabledLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); |
| | | measurementModeEnabledLabel.setForeground(Color.DARK_GRAY); |
| | | gbc = new GridBagConstraints(); |
| | | gbc.gridx = 1; |
| | | gbc.gridy = 0; |
| | | gbc.weightx = 1.0; |
| | | gbc.anchor = GridBagConstraints.EAST; |
| | | panel.add(measurementModeEnabledLabel, gbc); |
| | | |
| | | panel.putClientProperty("valueLabel", measurementModeEnabledLabel); |
| | | |
| | | // 创建切换按钮(使用图标) |
| | | JButton toggleBtn = createMeasurementModeToggleButton(); |
| | | gbc = new GridBagConstraints(); |
| | | gbc.gridx = 2; |
| | | gbc.gridy = 0; |
| | | gbc.weightx = 0; |
| | | gbc.anchor = GridBagConstraints.EAST; |
| | | panel.add(toggleBtn, gbc); |
| | | panel.putClientProperty("toggleButton", toggleBtn); |
| | | |
| | | return panel; |
| | | } |
| | | |
| | | /** |
| | | * 创建测量模式切换按钮 |
| | | */ |
| | | private JButton createMeasurementModeToggleButton() { |
| | | JButton button = new JButton(); |
| | | button.setContentAreaFilled(false); |
| | | button.setBorder(null); |
| | | button.setFocusPainted(false); |
| | | button.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | button.setPreferredSize(new Dimension(32, 32)); |
| | | button.setMinimumSize(new Dimension(32, 32)); |
| | | button.setMaximumSize(new Dimension(32, 32)); |
| | | |
| | | updateMeasurementModeToggleButton(button); |
| | | |
| | | button.addActionListener(e -> toggleMeasurementMode(button)); |
| | | |
| | | return button; |
| | | } |
| | | |
| | | /** |
| | | * 更新测量模式切换按钮图标 |
| | | */ |
| | | private void updateMeasurementModeToggleButton(JButton button) { |
| | | boolean isEnabled = setData.isMeasurementModeEnabled(); |
| | | try { |
| | | String iconPath = isEnabled ? "image/open.png" : "image/close.png"; |
| | | ImageIcon icon = new ImageIcon(iconPath); |
| | | if (icon.getIconWidth() > 0) { |
| | | Image scaledImage = icon.getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH); |
| | | button.setIcon(new ImageIcon(scaledImage)); |
| | | button.setText(null); |
| | | } else { |
| | | button.setIcon(null); |
| | | button.setText(isEnabled ? "开" : "关"); |
| | | } |
| | | } catch (Exception e) { |
| | | button.setIcon(null); |
| | | button.setText(isEnabled ? "开" : "关"); |
| | | System.err.println("无法加载测量模式图标: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 切换测量模式状态 |
| | | */ |
| | | private void toggleMeasurementMode(JButton button) { |
| | | boolean newValue = !setData.isMeasurementModeEnabled(); |
| | | setData.setMeasurementModeEnabled(newValue); |
| | | |
| | | // 保存到配置文件 |
| | | setData.updateProperty("measurementModeEnabled", String.valueOf(newValue)); |
| | | |
| | | // 更新UI |
| | | if (measurementModeEnabledLabel != null) { |
| | | measurementModeEnabledLabel.setText(newValue ? "已开启" : "已关闭"); |
| | | } |
| | | updateMeasurementModeToggleButton(button); |
| | | |
| | | // 通知MapRenderer更新 |
| | | Shouye shouye = Shouye.getInstance(); |
| | | if (shouye != null) { |
| | | MapRenderer renderer = shouye.getMapRenderer(); |
| | | if (renderer != null) { |
| | | renderer.setMeasurementMode(newValue); |
| | | } |
| | | if (newValue) { |
| | | celiangmoshi.start(); |
| | | } else { |
| | | celiangmoshi.stop(); |
| | | } |
| | | // 刷新地图显示(通过MapRenderer触发重绘) |
| | | if (renderer != null) { |
| | | renderer.repaint(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 创建边界距离显示切换按钮 |
| | | */ |
| | | private JButton createBoundaryLengthToggleButton() { |
| | |
| | | // 从Setsys类加载数据 |
| | | setData.initializeFromProperties(); |
| | | baseStation.load(); |
| | | // 从device.properties加载设备数据 |
| | | Device device = Device.getActiveDevice(); |
| | | if (device != null) { |
| | | device.initFromProperties(); |
| | | } |
| | | updateDisplay(); |
| | | // 加载并应用上次保存的视图中心坐标 |
| | | loadViewCenterFromProperties(); |
| | |
| | | mowerIdLabel.setText(setData.getMowerId() != null ? setData.getMowerId() : "未设置"); |
| | | } |
| | | |
| | | // 更新割草机长宽显示 |
| | | if (mowerSizeLabel != null) { |
| | | mowerSizeLabel.setText(formatMowerSize()); |
| | | } |
| | | |
| | | // 更新割草安全距离显示 |
| | | if (mowingSafetyDistanceLabel != null) { |
| | | mowingSafetyDistanceLabel.setText(formatMowingSafetyDistance()); |
| | | } |
| | | |
| | | if (baseStationIdLabel != null) { |
| | | baseStationIdLabel.setText(resolveBaseStationId()); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | // 更新测量模式状态 |
| | | if (measurementModeEnabledLabel != null) { |
| | | measurementModeEnabledLabel.setText(setData.isMeasurementModeEnabled() ? "已开启" : "已关闭"); |
| | | } |
| | | // 更新测量模式切换按钮图标 |
| | | JPanel measurementModePanel = (JPanel) measurementModeEnabledLabel.getParent(); |
| | | if (measurementModePanel != null) { |
| | | JButton toggleBtn = (JButton) measurementModePanel.getClientProperty("toggleButton"); |
| | | if (toggleBtn != null) { |
| | | updateMeasurementModeToggleButton(toggleBtn); |
| | | } |
| | | } |
| | | |
| | | // 更新APP版本显示 |
| | | if (appVersionLabel != null) { |
| | | appVersionLabel.setText(setData.getAppVersion() != null ? |
| | |
| | | mowerIdEditBtn.addActionListener(e -> editMowerId()); |
| | | } |
| | | |
| | | // 割草机长宽编辑按钮事件 |
| | | if (mowerSizeEditBtn != null) { |
| | | mowerSizeEditBtn.addActionListener(e -> editMowerSize()); |
| | | } |
| | | |
| | | // 割草安全距离编辑按钮事件 |
| | | if (mowingSafetyDistanceEditBtn != null) { |
| | | mowingSafetyDistanceEditBtn.addActionListener(e -> editMowingSafetyDistance()); |
| | | } |
| | | |
| | | if (baseStationIdEditBtn != null) { |
| | | baseStationIdEditBtn.addActionListener(e -> editBaseStationId()); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | private void editMowerSize() { |
| | | Device device = Device.getActiveDevice(); |
| | | if (device == null) { |
| | | JOptionPane.showMessageDialog(this, "设备未初始化", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 获取当前值并转换为米(兼容旧数据) |
| | | double currentLengthMeters = convertToMeters(device.getMowerLength()); |
| | | double currentWidthMeters = convertToMeters(device.getMowerWidth()); |
| | | |
| | | String currentLengthStr = currentLengthMeters >= 0 ? String.format("%.2f", currentLengthMeters) : ""; |
| | | String currentWidthStr = currentWidthMeters >= 0 ? String.format("%.2f", currentWidthMeters) : ""; |
| | | |
| | | // 创建输入对话框 |
| | | JPanel inputPanel = new JPanel(new GridLayout(2, 2, 5, 5)); |
| | | inputPanel.add(new JLabel("长度(m):")); |
| | | JTextField lengthField = new JTextField(currentLengthStr); |
| | | inputPanel.add(lengthField); |
| | | inputPanel.add(new JLabel("宽度(m):")); |
| | | JTextField widthField = new JTextField(currentWidthStr); |
| | | inputPanel.add(widthField); |
| | | |
| | | int result = JOptionPane.showConfirmDialog(this, |
| | | inputPanel, |
| | | "修改割草机长宽", |
| | | JOptionPane.OK_CANCEL_OPTION, |
| | | JOptionPane.QUESTION_MESSAGE); |
| | | |
| | | if (result == JOptionPane.OK_OPTION) { |
| | | String newLength = lengthField.getText().trim(); |
| | | String newWidth = widthField.getText().trim(); |
| | | |
| | | // 验证输入 |
| | | if (newLength.isEmpty() && newWidth.isEmpty()) { |
| | | JOptionPane.showMessageDialog(this, "长度和宽度不能同时为空", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | double lengthValue = -1; |
| | | double widthValue = -1; |
| | | |
| | | if (!newLength.isEmpty()) { |
| | | try { |
| | | lengthValue = Double.parseDouble(newLength); |
| | | if (lengthValue < 0) { |
| | | JOptionPane.showMessageDialog(this, "长度必须大于等于0", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | } catch (NumberFormatException e) { |
| | | JOptionPane.showMessageDialog(this, "长度必须是有效的数字", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | if (!newWidth.isEmpty()) { |
| | | try { |
| | | widthValue = Double.parseDouble(newWidth); |
| | | if (widthValue < 0) { |
| | | JOptionPane.showMessageDialog(this, "宽度必须大于等于0", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | } catch (NumberFormatException e) { |
| | | JOptionPane.showMessageDialog(this, "宽度必须是有效的数字", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // 更新设备属性(保存为米,保留2位小数) |
| | | if (lengthValue >= 0) { |
| | | String lengthStr = String.format("%.2f", lengthValue); |
| | | device.setMowerLength(lengthStr); |
| | | device.updateField("mowerLength", lengthStr); |
| | | } else { |
| | | device.setMowerLength("-1"); |
| | | device.updateField("mowerLength", "-1"); |
| | | } |
| | | |
| | | if (widthValue >= 0) { |
| | | String widthStr = String.format("%.2f", widthValue); |
| | | device.setMowerWidth(widthStr); |
| | | device.updateField("mowerWidth", widthStr); |
| | | } else { |
| | | device.setMowerWidth("-1"); |
| | | device.updateField("mowerWidth", "-1"); |
| | | } |
| | | |
| | | // 如果长度和宽度都有效,自动计算安全距离 |
| | | if (lengthValue > 0 && widthValue > 0) { |
| | | try { |
| | | double safetyDistance = MowerSafetyDistanceCalculator.calculateSafetyDistance(lengthValue, widthValue); |
| | | String safetyDistanceStr = String.format("%.2f", safetyDistance); |
| | | device.setMowingSafetyDistance(safetyDistanceStr); |
| | | device.updateField("mowingSafetyDistance", safetyDistanceStr); |
| | | |
| | | // 更新安全距离显示 |
| | | if (mowingSafetyDistanceLabel != null) { |
| | | mowingSafetyDistanceLabel.setText(formatMowingSafetyDistance()); |
| | | } |
| | | } catch (IllegalArgumentException e) { |
| | | // 如果计算失败,不更新安全距离 |
| | | System.err.println("计算安全距离失败: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | // 保存到device.properties |
| | | device.saveToProperties(); |
| | | |
| | | // 更新显示 |
| | | if (mowerSizeLabel != null) { |
| | | mowerSizeLabel.setText(formatMowerSize()); |
| | | } |
| | | JOptionPane.showMessageDialog(this, "割草机长宽更新成功" + |
| | | (lengthValue > 0 && widthValue > 0 ? ",安全距离已自动计算" : ""), |
| | | "成功", JOptionPane.INFORMATION_MESSAGE); |
| | | } |
| | | } |
| | | |
| | | private void editMowingSafetyDistance() { |
| | | Device device = Device.getActiveDevice(); |
| | | if (device == null) { |
| | | JOptionPane.showMessageDialog(this, "设备未初始化", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 获取当前值并转换为米(兼容旧数据) |
| | | double currentDistanceMeters = convertToMeters(device.getMowingSafetyDistance()); |
| | | String currentValueStr = currentDistanceMeters >= 0 ? String.format("%.2f", currentDistanceMeters) : ""; |
| | | |
| | | String newValue = (String) JOptionPane.showInputDialog(this, |
| | | "请输入割草安全距离(单位:m):", |
| | | "修改割草安全距离", |
| | | JOptionPane.QUESTION_MESSAGE, |
| | | null, |
| | | null, |
| | | currentValueStr); |
| | | |
| | | if (newValue == null) { |
| | | return; // 用户取消 |
| | | } |
| | | |
| | | newValue = newValue.trim(); |
| | | if (newValue.isEmpty()) { |
| | | JOptionPane.showMessageDialog(this, "割草安全距离不能为空", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 验证输入 |
| | | double distanceValue; |
| | | try { |
| | | distanceValue = Double.parseDouble(newValue); |
| | | if (distanceValue < 0) { |
| | | JOptionPane.showMessageDialog(this, "割草安全距离必须大于等于0", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | } catch (NumberFormatException e) { |
| | | JOptionPane.showMessageDialog(this, "割草安全距离必须是有效的数字", "提示", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 更新设备属性(保存为米,保留2位小数) |
| | | String distanceStr = String.format("%.2f", distanceValue); |
| | | device.setMowingSafetyDistance(distanceStr); |
| | | device.updateField("mowingSafetyDistance", distanceStr); |
| | | |
| | | // 保存到device.properties |
| | | device.saveToProperties(); |
| | | |
| | | // 更新显示 |
| | | if (mowingSafetyDistanceLabel != null) { |
| | | mowingSafetyDistanceLabel.setText(formatMowingSafetyDistance()); |
| | | } |
| | | JOptionPane.showMessageDialog(this, "割草安全距离更新成功", "成功", JOptionPane.INFORMATION_MESSAGE); |
| | | } |
| | | |
| | | private void editHandheldMarkerId() { |
| | | String currentValue = setData.getHandheldMarkerId() != null ? setData.getHandheldMarkerId() : ""; |
| | | String newValue = (String) JOptionPane.showInputDialog(this, |