张世豪
6 天以前 8ce07ce9a4034fdc959d280dd38ecb3e05cbe6e1
src/dikuai/addzhangaiwu.java
@@ -10,8 +10,11 @@
import java.awt.Font;
import java.awt.Image;
import java.awt.Window;
import java.awt.Container;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
@@ -46,10 +49,13 @@
import set.Setsys;
import ui.UIConfig;
import zhuye.Coordinate;
import zhuye.MapRenderer;
import zhuye.Shouye;
import zhangaiwu.AddDikuai;
import zhangaiwu.Obstacledge;
import zhangaiwu.yulanzhangaiwu;
import zhuye.buttonset;
import bianjie.bianjieguihua2;
/**
 * 障碍物新增/编辑对话框。设计语言参考 {@link AddDikuai},支持通过实地绘制采集障碍物坐标。
@@ -86,7 +92,10 @@
    private JPanel selectedMethodPanel;
    private JPanel selectedShapePanel;
    private JButton drawButton;
    private JButton generateBoundaryButton;  // 生成障碍物边界按钮
    private JButton previewButton;  // 预览按钮
    private JLabel drawingStatusLabel;
    private JLabel boundaryStatusLabel;  // 生成障碍物边界状态标签
    private JTextField obstacleNameField;
    private JPanel existingObstacleListPanel;
    private JPanel step1NextButtonRow;
@@ -286,13 +295,10 @@
    }
    private JButton createInlineButton(String text) {
        JButton button = new JButton(text);
        JButton button = buttonset.createStyledButton(text, PRIMARY_COLOR);
        button.setFont(new Font("微软雅黑", Font.BOLD, 12));
        button.setForeground(WHITE);
        button.setBackground(PRIMARY_COLOR);
        button.setBorder(BorderFactory.createEmptyBorder(6, 16, 6, 16));
        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        button.setFocusPainted(false);
        Dimension size = new Dimension(72, 28);
        button.setPreferredSize(size);
        button.setMinimumSize(size);
@@ -491,6 +497,7 @@
            formData.remove("editingObstacleName");
            formData.remove("obstacleCoordinates");
            formData.remove("obstacleOriginalCoordinates");
            formData.remove("generatedBoundaryCoordinates");  // 清除生成的边界坐标
            if (obstacleNameField != null) {
                obstacleNameField.setText("");
            } else {
@@ -498,6 +505,7 @@
            }
            updateDrawingStatus();
            updateSaveButtonState();
            updatePreviewButtonState();
        }
    }
@@ -540,10 +548,24 @@
        stepPanel.add(Box.createRigidArea(new Dimension(0, 24)));
        // 按钮容器:重新绘制 + 生成障碍物边界
        JPanel buttonRow = new JPanel();
        buttonRow.setLayout(new BoxLayout(buttonRow, BoxLayout.X_AXIS));
        buttonRow.setOpaque(false);
        buttonRow.setAlignmentX(Component.LEFT_ALIGNMENT);
        drawButton = createPrimaryButton("开始绘制", 16);
        drawButton.setAlignmentX(Component.LEFT_ALIGNMENT);
        drawButton.addActionListener(e -> startDrawingWorkflow());
        stepPanel.add(drawButton);
        buttonRow.add(drawButton);
        buttonRow.add(Box.createRigidArea(new Dimension(10, 0)));
        generateBoundaryButton = createPrimaryButton("生成障碍物边界", 16);
        generateBoundaryButton.setVisible(false);  // 初始隐藏,绘制完成后显示
        generateBoundaryButton.addActionListener(e -> generateObstacleBoundary());
        buttonRow.add(generateBoundaryButton);
        stepPanel.add(buttonRow);
        stepPanel.add(Box.createRigidArea(new Dimension(0, 12)));
@@ -552,6 +574,15 @@
        drawingStatusLabel.setForeground(LIGHT_TEXT);
        drawingStatusLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
        stepPanel.add(drawingStatusLabel);
        // 添加生成障碍物边界状态标签
        stepPanel.add(Box.createRigidArea(new Dimension(0, 8)));
        boundaryStatusLabel = new JLabel("");
        boundaryStatusLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
        boundaryStatusLabel.setForeground(LIGHT_TEXT);
        boundaryStatusLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
        boundaryStatusLabel.setVisible(false);  // 初始隐藏
        stepPanel.add(boundaryStatusLabel);
        stepPanel.add(Box.createVerticalGlue());
        return stepPanel;
@@ -565,12 +596,24 @@
        prevButton = createSecondaryButton("上一步");
        nextButton = createPrimaryButton("下一步", 16);
        // 设置下一步按钮宽度为300像素
        nextButton.setPreferredSize(new Dimension(300, nextButton.getPreferredSize().height));
        nextButton.setMaximumSize(new Dimension(300, nextButton.getPreferredSize().height));
        previewButton = createSecondaryButton("预览");
        previewButton.setVisible(false);  // 初始隐藏,生成边界后显示
        previewButton.setOpaque(true);
        previewButton.setContentAreaFilled(true);
        previewButton.addActionListener(e -> previewObstacleBoundary());
        saveButton = createPrimaryButton("保存", 16);
        saveButton.setVisible(false);
        buttonPanel.add(prevButton);
        buttonPanel.add(Box.createHorizontalGlue());
        buttonPanel.add(Box.createRigidArea(new Dimension(12, 0)));
        buttonPanel.add(previewButton);
        buttonPanel.add(Box.createRigidArea(new Dimension(10, 0)));
        buttonPanel.add(saveButton);
        attachNextButtonToStep1Row();
@@ -752,6 +795,10 @@
        if (option == null) {
            return;
        }
        if (userTriggered && "handheld".equalsIgnoreCase(type) && !hasConfiguredHandheldMarker()) {
            JOptionPane.showMessageDialog(this, "请先添加便携打点器编号", "提示", JOptionPane.WARNING_MESSAGE);
            return;
        }
        if (selectedMethodPanel != null && selectedMethodPanel != option) {
            resetOptionAppearance(selectedMethodPanel);
        }
@@ -763,6 +810,11 @@
        }
    }
    private boolean hasConfiguredHandheldMarker() {
        String handheldId = Setsys.getPropertyValue("handheldMarkerId");
        return handheldId != null && !handheldId.trim().isEmpty();
    }
    private void selectShapeOption(JPanel option, String type, boolean userTriggered) {
        if (option == null) {
            return;
@@ -858,6 +910,17 @@
        }
        activeDrawingShape = null;
        // 重新绘制时清除之前生成的边界坐标
        formData.remove("generatedBoundaryCoordinates");
        if (previewButton != null) {
            previewButton.setVisible(false);
        }
        // 清除边界状态标签
        if (boundaryStatusLabel != null) {
            boundaryStatusLabel.setVisible(false);
            boundaryStatusLabel.setText("");
        }
        String method = formData.get("drawingMethod");
        if (!isMeaningfulValue(method)) {
@@ -887,6 +950,15 @@
        activeDrawingShape = shape.toLowerCase(Locale.ROOT);
        Shouye shouyeInstance = Shouye.getInstance();
        if (shouyeInstance != null) {
            shouyeInstance.setHandheldMowerIconActive("handheld".equalsIgnoreCase(method));
            MapRenderer renderer = shouyeInstance.getMapRenderer();
            if (renderer != null) {
                renderer.clearIdleTrail();
            }
        }
        Coordinate.coordinates.clear();
        Coordinate.setActiveDeviceIdFilter(deviceId);
        Coordinate.setStartSaveGngga(true);
@@ -1348,8 +1420,15 @@
    private void preloadData() {
        formData.remove("obstacleCoordinates");
        formData.remove("obstacleOriginalCoordinates");
        formData.remove("generatedBoundaryCoordinates");  // 清除生成的边界坐标
        formData.remove("editingObstacleName");
        updateDrawingStatus();
        updatePreviewButtonState();
        // 清除边界状态标签
        if (boundaryStatusLabel != null) {
            boundaryStatusLabel.setVisible(false);
            boundaryStatusLabel.setText("");
        }
    }
    private void updateDrawingStatus() {
@@ -1364,14 +1443,113 @@
                drawButton.setText("重新绘制");
                drawButton.setEnabled(true);
            }
            // 绘制完成后显示"生成障碍物边界"按钮
            if (generateBoundaryButton != null) {
                generateBoundaryButton.setVisible(true);
            }
        } else {
            drawingStatusLabel.setText("尚未采集障碍物坐标");
            if (!drawingInProgress && drawButton != null) {
                drawButton.setText("开始绘制");
                drawButton.setEnabled(true);
            }
            // 未绘制时隐藏"生成障碍物边界"按钮
            if (generateBoundaryButton != null) {
                generateBoundaryButton.setVisible(false);
            }
            // 未生成边界时隐藏预览按钮
            if (previewButton != null) {
                previewButton.setVisible(false);
            }
            // 隐藏边界状态标签
            if (boundaryStatusLabel != null) {
                boundaryStatusLabel.setVisible(false);
            }
        }
        updateSaveButtonState();
        updatePreviewButtonState();
    }
    /**
     * 更新边界状态标签
     */
    private void updateBoundaryStatusLabel(int pointCount) {
        if (boundaryStatusLabel == null) {
            return;
        }
        if (pointCount > 0) {
            boundaryStatusLabel.setText("生成障碍物边界点数:" + pointCount);
            boundaryStatusLabel.setVisible(true);
        } else {
            boundaryStatusLabel.setText("");
            boundaryStatusLabel.setVisible(false);
        }
    }
    /**
     * 更新预览按钮的显示状态
     */
    private void updatePreviewButtonState() {
        if (previewButton == null) {
            return;
        }
        // 只有在生成边界后才显示预览按钮
        String generatedBoundary = formData.get("generatedBoundaryCoordinates");
        boolean hasGeneratedBoundary = isMeaningfulValue(generatedBoundary);
        if (hasGeneratedBoundary) {
            // 生成边界后,重新创建绿色的预览按钮
            // 获取按钮的父容器和位置
            Container parent = previewButton.getParent();
            if (parent != null) {
                // 保存原有的ActionListener
                ActionListener[] listeners = previewButton.getActionListeners();
                // 移除旧按钮
                parent.remove(previewButton);
                // 创建新的绿色预览按钮(使用主按钮样式)
                previewButton = createPrimaryButton("预览", 16);
                previewButton.setVisible(true);
                previewButton.setEnabled(true);
                // 恢复ActionListener
                for (ActionListener listener : listeners) {
                    previewButton.addActionListener(listener);
                }
                // 添加到父容器(在保存按钮之前)
                int previewIndex = -1;
                Component[] components = parent.getComponents();
                for (int i = 0; i < components.length; i++) {
                    if (components[i] instanceof JButton) {
                        JButton btn = (JButton) components[i];
                        if ("保存".equals(btn.getText())) {
                            previewIndex = i;
                            break;
                        }
                    }
                }
                if (previewIndex >= 0) {
                    parent.add(previewButton, previewIndex);
                    parent.add(Box.createRigidArea(new Dimension(10, 0)), previewIndex + 1);
                } else {
                    // 如果找不到保存按钮,添加到末尾
                    parent.add(Box.createRigidArea(new Dimension(12, 0)));
                    parent.add(previewButton);
                    parent.add(Box.createRigidArea(new Dimension(10, 0)));
                }
                // 刷新布局
                parent.revalidate();
                parent.repaint();
            }
        } else {
            // 未生成边界时,隐藏按钮
            previewButton.setVisible(false);
            previewButton.setEnabled(false);
        }
    }
    private int countCoordinatePairs(String coords) {
@@ -1399,16 +1577,32 @@
        if (saveButton != null) {
            boolean hasCoords = isMeaningfulValue(formData.get("obstacleCoordinates"));
            boolean hasName = isMeaningfulValue(formData.get("obstacleName"));
            boolean enabled = hasCoords && hasName;
            // 检查是否生成了障碍物边界坐标
            String shapeKey = formData.get("obstacleShape");
            boolean hasGeneratedBoundary = isMeaningfulValue(formData.get("generatedBoundaryCoordinates"));
            // 如果是多边形障碍物,必须生成边界坐标才能保存
            // 如果是圆形障碍物或其他形状,不需要生成边界就能保存
            boolean boundaryRequirementMet = true;
            if ("polygon".equals(shapeKey)) {
                boundaryRequirementMet = hasGeneratedBoundary;
            }
            boolean enabled = hasCoords && hasName && boundaryRequirementMet;
            saveButton.setEnabled(enabled);
            if (enabled) {
                saveButton.setBackground(PRIMARY_COLOR);
                saveButton.setForeground(WHITE);
                saveButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                saveButton.setOpaque(true);
                saveButton.setContentAreaFilled(true);
            } else {
                saveButton.setBackground(MEDIUM_GRAY);
                saveButton.setForeground(TEXT_COLOR);
                saveButton.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                saveButton.setOpaque(true);
                saveButton.setContentAreaFilled(true);
            }
        }
    }
@@ -1446,6 +1640,7 @@
        String obstacleName = formData.get("obstacleName");
        String coordsValue = formData.get("obstacleCoordinates");
        String originalValue = formData.get("obstacleOriginalCoordinates");
        String generatedBoundaryValue = formData.get("generatedBoundaryCoordinates");  // 生成的边界坐标
        String shapeKey = formData.get("obstacleShape");
        String previousName = formData.get("editingObstacleName");
@@ -1456,6 +1651,8 @@
        String coords = coordsValue.trim();
        String originalCoords = isMeaningfulValue(originalValue) ? originalValue.trim() : null;
        // 如果有生成的边界坐标,使用生成的边界坐标;否则使用原始坐标
        String finalCoords = isMeaningfulValue(generatedBoundaryValue) ? generatedBoundaryValue.trim() : coords;
        String trimmedName = isMeaningfulValue(obstacleName) ? obstacleName.trim() : null;
        if (!isMeaningfulValue(trimmedName)) {
            JOptionPane.showMessageDialog(this, "障碍物名称无效", "错误", JOptionPane.ERROR_MESSAGE);
@@ -1467,7 +1664,7 @@
            formData.put("editingObstacleName", trimmedName);
        }
        if (!persistObstacleToConfig(landNumber, previousName, trimmedName, shapeKey, coords, originalCoords)) {
        if (!persistObstacleToConfig(landNumber, previousName, trimmedName, shapeKey, finalCoords, originalCoords)) {
            JOptionPane.showMessageDialog(this, "写入障碍物配置失败,请重试", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }
@@ -1479,6 +1676,171 @@
        activeSession = null;
        dispose();
    }
    /**
     * 生成障碍物边界坐标
     */
    private void generateObstacleBoundary() {
        String originalCoords = formData.get("obstacleOriginalCoordinates");
        if (!isMeaningfulValue(originalCoords)) {
            JOptionPane.showMessageDialog(this, "无原始坐标数据,无法生成边界", "提示", JOptionPane.INFORMATION_MESSAGE);
            return;
        }
        String baseStation = targetDikuai.getBaseStationCoordinates();
        if (!isMeaningfulValue(baseStation)) {
            JOptionPane.showMessageDialog(this, "地块未设置基站坐标", "提示", JOptionPane.WARNING_MESSAGE);
            return;
        }
        String shapeKey = formData.get("obstacleShape");
        if (!"polygon".equals(shapeKey)) {
            JOptionPane.showMessageDialog(this, "只有多边形障碍物才需要生成边界坐标", "提示", JOptionPane.INFORMATION_MESSAGE);
            return;
        }
        try {
            // 检查绘制方式,只有割草机绘制的多边形才调用bianjieguihua2
            String method = formData.get("drawingMethod");
            if (!"mower".equals(method)) {
                JOptionPane.showMessageDialog(this, "只有割草机绘制的多边形才支持生成边界坐标", "提示", JOptionPane.INFORMATION_MESSAGE);
                return;
            }
            // 将原始坐标转换为Coordinate对象列表
            List<Coordinate> coordinateList = parseOriginalCoordinatesToCoordinateList(originalCoords);
            if (coordinateList.isEmpty()) {
                JOptionPane.showMessageDialog(this, "原始坐标数据无效", "错误", JOptionPane.ERROR_MESSAGE);
                return;
            }
            // 保存当前的Coordinate.coordinates
            List<Coordinate> savedCoordinates = new ArrayList<>(Coordinate.coordinates);
            try {
                // 设置到全局坐标列表
                Coordinate.coordinates.clear();
                Coordinate.coordinates.addAll(coordinateList);
                // 调用bianjieguihua2算法生成优化后的多边形边界坐标
                String optimizedCoordsStr = bianjieguihua2.processCoordinateListAuto(baseStation);
                if (optimizedCoordsStr == null || optimizedCoordsStr.trim().isEmpty()) {
                    JOptionPane.showMessageDialog(this, "生成边界坐标失败", "错误", JOptionPane.ERROR_MESSAGE);
                    return;
                }
                // 保存生成的边界坐标
                formData.put("generatedBoundaryCoordinates", optimizedCoordsStr.trim());
                // 计算生成的边界点数
                int boundaryPointCount = countCoordinatePairs(optimizedCoordsStr.trim());
                // 更新边界状态标签显示
                updateBoundaryStatusLabel(boundaryPointCount);
                // 更新预览按钮显示(变成绿色可点击)
                updatePreviewButtonState();
                // 更新保存按钮状态(变成可点击)
                updateSaveButtonState();
                // 强制刷新UI
                SwingUtilities.invokeLater(() -> {
                    if (previewButton != null) {
                        previewButton.revalidate();
                        previewButton.repaint();
                    }
                });
                JOptionPane.showMessageDialog(this, "障碍物边界坐标已生成", "成功", JOptionPane.INFORMATION_MESSAGE);
            } finally {
                // 恢复原来的坐标列表
                Coordinate.coordinates.clear();
                Coordinate.coordinates.addAll(savedCoordinates);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            JOptionPane.showMessageDialog(this, "生成边界坐标时发生错误: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
        }
    }
    /**
     * 将原始坐标字符串解析为Coordinate对象列表
     */
    private List<Coordinate> parseOriginalCoordinatesToCoordinateList(String originalCoords) {
        List<Coordinate> coordinateList = new ArrayList<>();
        if (!isMeaningfulValue(originalCoords)) {
            return coordinateList;
        }
        // 原始坐标格式:纬度1,方向1,经度1,方向1;纬度2,方向2,经度2,方向2;...
        String[] pointStrings = originalCoords.split(";");
        for (String pointStr : pointStrings) {
            pointStr = pointStr.trim();
            if (pointStr.isEmpty()) continue;
            String[] parts = pointStr.split(",");
            if (parts.length >= 4) {
                try {
                    String lat = parts[0].trim();
                    String latDir = parts[1].trim();
                    String lon = parts[2].trim();
                    String lonDir = parts[3].trim();
                    Coordinate coord = new Coordinate(lat, latDir, lon, lonDir, 0.0);
                    coordinateList.add(coord);
                } catch (Exception e) {
                    // 跳过无效的坐标点
                    continue;
                }
            }
        }
        return coordinateList;
    }
    /**
     * 预览障碍物边界
     */
    private void previewObstacleBoundary() {
        String generatedBoundary = formData.get("generatedBoundaryCoordinates");
        if (!isMeaningfulValue(generatedBoundary)) {
            JOptionPane.showMessageDialog(this, "请先生成障碍物边界坐标", "提示", JOptionPane.INFORMATION_MESSAGE);
            return;
        }
        String landNumber = targetDikuai.getLandNumber();
        String landName = targetDikuai.getLandName();
        String boundary = targetDikuai.getBoundaryCoordinates();
        // 关闭当前对话框
        setVisible(false);
        SwingUtilities.invokeLater(() -> {
            Shouye shouye = Shouye.getInstance();
            if (shouye != null) {
                // 传递回调以重新打开新增障碍物步骤2页面
                shouye.startMowingPathPreview(
                    landNumber,
                    landName,
                    boundary,
                    generatedBoundary,  // 使用生成的边界坐标作为障碍物坐标
                    null,
                    () -> SwingUtilities.invokeLater(() -> {
                        // 重新打开新增障碍物步骤2页面
                        Window owner = SwingUtilities.getWindowAncestor(shouye);
                        setVisible(true);
                    })
                );
            } else {
                JOptionPane.showMessageDialog(null, "无法打开主页面进行预览", "提示", JOptionPane.WARNING_MESSAGE);
                setVisible(true);
            }
        });
    }
    private boolean persistObstacleToConfig(String landNumber, String previousName, String obstacleName,
                                            String shapeKey, String xyCoords, String originalCoords) {
@@ -1686,10 +2048,14 @@
        if (step < 2) {
            nextButton.setVisible(true);
            saveButton.setVisible(false);
            if (previewButton != null) {
                previewButton.setVisible(false);
            }
        } else {
            nextButton.setVisible(false);
            saveButton.setVisible(true);
            updateDrawingStatus();
            updatePreviewButtonState();
        }
        updateSaveButtonState();
        revalidate();
@@ -1733,6 +2099,24 @@
        }
        updateDrawingStatus();
        // 如果已有生成的边界坐标,更新边界状态标签
        String generatedBoundary = session.data.get("generatedBoundaryCoordinates");
        if (isMeaningfulValue(generatedBoundary)) {
            int boundaryPointCount = countCoordinatePairs(generatedBoundary);
            updateBoundaryStatusLabel(boundaryPointCount);
        } else {
            // 如果没有生成的边界坐标,隐藏边界状态标签
            if (boundaryStatusLabel != null) {
                boundaryStatusLabel.setVisible(false);
                boundaryStatusLabel.setText("");
            }
        }
        // 更新按钮状态
        updatePreviewButtonState();
        updateSaveButtonState();
        currentStep = 2;
        showStep(2);
    }
@@ -1856,15 +2240,14 @@
    }
    private JButton createPrimaryButton(String text, int fontSize) {
        JButton button = new JButton(text);
        JButton button = buttonset.createStyledButton(text, PRIMARY_COLOR);
        button.setFont(new Font("微软雅黑", Font.BOLD, fontSize));
        button.setBackground(PRIMARY_COLOR);
        button.setForeground(WHITE);
        button.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(PRIMARY_DARK, 2),
                BorderFactory.createEmptyBorder(10, 22, 10, 22)));
        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        button.setFocusPainted(false);
        button.setOpaque(true);
        button.setContentAreaFilled(true);
        button.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseEntered(MouseEvent e) {
@@ -1877,6 +2260,9 @@
            public void mouseExited(MouseEvent e) {
                if (button.isEnabled()) {
                    button.setBackground(PRIMARY_COLOR);
                } else {
                    // 禁用时保持灰色背景
                    button.setBackground(MEDIUM_GRAY);
                }
            }
        });
@@ -1884,15 +2270,15 @@
    }
    private JButton createSecondaryButton(String text) {
        JButton button = new JButton(text);
        JButton button = buttonset.createStyledButton(text, MEDIUM_GRAY);
        button.setFont(new Font("微软雅黑", Font.BOLD, 16));
        button.setBackground(MEDIUM_GRAY);
        button.setForeground(TEXT_COLOR);
        button.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(BORDER_COLOR, 2),
                BorderFactory.createEmptyBorder(10, 22, 10, 22)));
        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        button.setFocusPainted(false);
        button.setOpaque(true);
        button.setContentAreaFilled(true);
        return button;
    }