张世豪
18 小时以前 1175f5fbe8fd832943880bfc37c0e2a451a0688a
src/lujing/MowingPathGenerationPage.java
@@ -1,25 +1,12 @@
package lujing;
import javax.swing.*;
import javax.swing.SwingUtilities;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import dikuai.Dikuai;
import lujing.Lunjingguihua;
import lujing.ObstaclePathPlanner;
import lujing.Qufenxingzhuang;
import lujing.AoxinglujingNoObstacle;
import lujing.YixinglujingNoObstacle;
import publicway.Fuzhibutton;
import lujing.AoxinglujingHaveObstacel;
import lujing.YixinglujingHaveObstacel;
import org.locationtech.jts.geom.Coordinate;
import gecaoji.Device;
import java.util.Locale;
@@ -49,6 +36,7 @@
        boolean saveObstacleCoordinates(Dikuai dikuai, String baseStationValue, String obstacleValue);
        boolean saveMowingWidth(Dikuai dikuai, String value);
        boolean savePlannedPath(Dikuai dikuai, String value);
        boolean saveMowingSafetyDistance(Dikuai dikuai, String value);
    }
    
    private final Dikuai dikuai;
@@ -262,30 +250,35 @@
     * 预览路径
     */
    private void previewPath() {
        // 先保存当前路径到地块(临时保存,用于预览)
        String pathNormalized = normalizeCoordinateInput(pathArea.getText());
        // 直接从文本域获取路径数据
        String rawPath = pathArea.getText();
        String pathNormalized = normalizeCoordinateInput(rawPath);
        if (!"-1".equals(pathNormalized)) {
            // 规范化路径数据:支持换行、空格等分隔符
            pathNormalized = pathNormalized
                .replace("\r\n", ";")
                .replace('\r', ';')
                .replace('\n', ';')
                .replaceAll(";+", ";")
                .replaceAll("\\s*;\\s*", ";")
                .trim();
                .replaceAll("\\s+", ";") // 将所有空白字符替换为分号
                .replaceAll(";+", ";");  // 合并连续分号
            // 去除首尾分号
            if (pathNormalized.startsWith(";")) pathNormalized = pathNormalized.substring(1);
            if (pathNormalized.endsWith(";")) pathNormalized = pathNormalized.substring(0, pathNormalized.length() - 1);
            if (pathNormalized.isEmpty()) {
                pathNormalized = "-1";
            }
        }
        
        if ("-1".equals(pathNormalized)) {
            JOptionPane.showMessageDialog(this, "请先生成割草路径", "提示", JOptionPane.INFORMATION_MESSAGE);
            JOptionPane.showMessageDialog(this, "请先生成割草路径或在文本框中输入有效坐标", "提示", JOptionPane.INFORMATION_MESSAGE);
            return;
        }
        
        // 临时保存路径到地块对象(不持久化)
        if (saveCallback != null) {
            saveCallback.savePlannedPath(dikuai, pathNormalized);
        }
        // 注意:预览时不自动保存路径到地块,仅使用文本域中的数据进行预览
        // 只有点击"保存路径"按钮时才持久化数据
        
        // 保存当前页面状态,用于返回时恢复
        String currentBaseStation = baseStationField.getText();
@@ -302,12 +295,16 @@
        String boundaryInput = normalizeCoordinateInput(boundaryArea.getText());
        final String boundary;
        if (!"-1".equals(boundaryInput)) {
            String processed = boundaryInput.replace("\r\n", ";")
            String processed = boundaryInput
                .replace("\r\n", ";")
                .replace('\r', ';')
                .replace('\n', ';')
                .replaceAll(";+", ";")
                .replaceAll("\\s*;\\s*", ";")
                .trim();
                .replaceAll("\\s+", ";")
                .replaceAll(";+", ";");
            if (processed.startsWith(";")) processed = processed.substring(1);
            if (processed.endsWith(";")) processed = processed.substring(0, processed.length() - 1);
            if (processed.isEmpty()) {
                boundary = dikuai.getBoundaryCoordinates();
            } else {
@@ -449,6 +446,21 @@
            JOptionPane.showMessageDialog(this, "请先生成割草路径", "提示", JOptionPane.INFORMATION_MESSAGE);
            return;
        }
        // 更新当前地块对象的属性
        if (dikuai != null) {
            dikuai.setBaseStationCoordinates(baseStationNormalized);
            dikuai.setBoundaryCoordinates(boundaryNormalized);
            dikuai.setMowingWidth(widthNormalized);
            dikuai.setPlannedPath(pathNormalized);
            dikuai.setObstacleCoordinates(obstacleNormalized);
            // 获取并更新安全距离
            String safetyDistance = getSafetyDistanceString();
            if (safetyDistance != null) {
                dikuai.setMowingSafetyDistance(safetyDistance);
            }
        }
        
        // 调用回调保存数据
        if (saveCallback != null) {
@@ -472,6 +484,15 @@
                JOptionPane.showMessageDialog(this, "无法保存割草路径", "错误", JOptionPane.ERROR_MESSAGE);
                return;
            }
            // 保存安全距离
            String safetyDistance = getSafetyDistanceString();
            if (safetyDistance != null) {
                if (!saveCallback.saveMowingSafetyDistance(dikuai, safetyDistance)) {
                    JOptionPane.showMessageDialog(this, "无法保存割草安全距离", "错误", JOptionPane.ERROR_MESSAGE);
                    return;
                }
            }
        }
        
        JOptionPane.showMessageDialog(this, "割草路径已保存", "成功", JOptionPane.INFORMATION_MESSAGE);
@@ -534,7 +555,20 @@
        
        String obstacles = sanitizeValueOrNull(obstacleInput);
        if (obstacles != null) {
            obstacles = obstacles.replace("\r\n", " ").replace('\r', ' ').replace('\n', ' ');
            // 按照用户要求,多个障碍物之间用 $ 符号分隔
            // 如果输入中包含 $,则保留 $,否则将换行符替换为 $
            if (obstacles.contains("$")) {
                // 已经是 $ 分隔的格式,只需清理换行符
                obstacles = obstacles.replace("\r\n", "").replace('\r', ' ').replace('\n', ' ');
            } else {
                // 尝试将换行符转换为 $,或者如果是一行则保持原样
                // 这里假设用户可能用换行分隔多个障碍物
                // 但根据需求描述,似乎输入本身就应该是 $ 分隔的,或者我们需要处理成 $ 分隔
                // 为了兼容性,如果用户输入的是换行分隔的多个障碍物,我们将其转换为 $ 分隔
                // 但通常障碍物坐标是一串坐标点,如果用户没有显式用 $ 分隔,我们很难区分是同一个障碍物的点还是多个障碍物
                // 因此,这里主要处理清理工作,具体的解析逻辑在各实现类中处理
                obstacles = obstacles.replace("\r\n", " ").replace('\r', ' ').replace('\n', ' ');
            }
        }
        // 获取安全距离
@@ -555,19 +589,10 @@
            int grassType = shapeJudger.judgeGrassType(boundary);
            // grassType: 0=无法判断, 1=凸形, 2=异形
            
            // 解析障碍物列表
            List<List<Coordinate>> obstacleList = Lunjingguihua.parseObstacles(obstacles);
            if (obstacleList == null) {
                obstacleList = new ArrayList<>();
            }
            // 判断是否有有效的障碍物:只有当解析成功且列表不为空时,才认为有障碍物
            boolean hasValidObstacles = !obstacleList.isEmpty();
            String generated = null;
            
            // 2. 根据地块类型和是否有障碍物,调用不同的路径生成类
            if (!hasValidObstacles) {
            if (!hasObstacleInput) {
                // 无障碍物的情况
                if (grassType == 1) {
                    // 凸形地块,无障碍物 -> 调用 AoxinglujingNoObstacle
@@ -576,100 +601,43 @@
                    generated = formatAoxingPathSegments(segments);
                } else if (grassType == 2) {
                    // 异形地块,无障碍物 -> 调用 YixinglujingNoObstacle
                    // 注意:如果该类还没有实现,这里会抛出异常或返回null
                    try {
                        // 调用 YixinglujingNoObstacle.planPath 获取路径段列表
                        List<YixinglujingNoObstacle.PathSegment> segments =
                            YixinglujingNoObstacle.planPath(boundary, plannerWidth, safetyMarginStr);
                        // 格式化路径段列表为字符串
                        generated = formatYixingPathSegments(segments);
                    } catch (Exception e) {
                        // 如果类还没有实现,使用原来的方法作为后备
                        if (showMessages) {
                            System.err.println("YixinglujingNoObstacle 尚未实现,使用默认方法: " + e.getMessage());
                        }
                        generated = Lunjingguihua.generatePathFromStrings(
                            boundary, obstacles != null ? obstacles : "", plannerWidth, safetyMarginStr, mode);
                    }
                    // 调用 YixinglujingNoObstacle.planPath 获取路径段列表
                    List<YixinglujingNoObstacle.PathSegment> segments =
                        YixinglujingNoObstacle.planPath(boundary, plannerWidth, safetyMarginStr);
                    // 格式化路径段列表为字符串
                    generated = formatYixingPathSegments(segments);
                } else {
                    // 无法判断地块类型,使用原来的方法作为后备
                    // 无法判断地块类型,默认按凸形处理或提示
                    if (showMessages) {
                        JOptionPane.showMessageDialog(parentComponent, "无法判断地块类型,使用默认路径生成方法",
                        JOptionPane.showMessageDialog(parentComponent, "无法判断地块类型,尝试按凸形地块处理",
                            "提示", JOptionPane.WARNING_MESSAGE);
                    }
                    generated = Lunjingguihua.generatePathFromStrings(
                        boundary, obstacles != null ? obstacles : "", plannerWidth, safetyMarginStr, mode);
                    List<AoxinglujingNoObstacle.PathSegment> segments =
                        AoxinglujingNoObstacle.planPath(boundary, plannerWidth, safetyMarginStr);
                    generated = formatAoxingPathSegments(segments);
                }
            } else {
                // 有障碍物的情况
                if (grassType == 1) {
                    // 凸形地块,有障碍物 -> 调用 AoxinglujingHaveObstacel
                    try {
                        // 假设 AoxinglujingHaveObstacel 有类似的方法签名
                        List<AoxinglujingHaveObstacel.PathSegment> segments = AoxinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
                        generated = formatAoxingHaveObstaclePathSegments(segments);
                    } catch (Exception e) {
                        // 如果类还没有实现,使用原来的方法作为后备
                        if (showMessages) {
                            System.err.println("AoxinglujingHaveObstacel 尚未实现,使用默认方法: " + e.getMessage());
                        }
                        List<Coordinate> polygon = Lunjingguihua.parseCoordinates(boundary);
                        if (polygon.size() < 4) {
                            if (showMessages) {
                                JOptionPane.showMessageDialog(parentComponent, "多边形坐标数量不足,至少需要三个点",
                                    "错误", JOptionPane.ERROR_MESSAGE);
                            }
                            return null;
                        }
                        double safetyDistance = Double.parseDouble(safetyMarginStr);
                        ObstaclePathPlanner pathPlanner = new ObstaclePathPlanner(
                            polygon, widthMeters, mode, obstacleList, safetyDistance);
                        List<Lunjingguihua.PathSegment> segments = pathPlanner.generate();
                        generated = Lunjingguihua.formatPathSegments(segments);
                    }
                    // 传入参数:boundary(A), obstacles(B), plannerWidth(C), safetyMarginStr(D)
                    List<AoxinglujingHaveObstacel.PathSegment> segments =
                        AoxinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
                    generated = formatAoxingHaveObstaclePathSegments(segments);
                } else if (grassType == 2) {
                    // 异形地块,有障碍物 -> 调用 YixinglujingHaveObstacel
                    try {
                        // 假设 YixinglujingHaveObstacel 有类似的方法签名
                        generated = YixinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
                    } catch (Exception e) {
                        // 如果类还没有实现,使用原来的方法作为后备
                        if (showMessages) {
                            System.err.println("YixinglujingHaveObstacel 尚未实现,使用默认方法: " + e.getMessage());
                        }
                        List<Coordinate> polygon = Lunjingguihua.parseCoordinates(boundary);
                        if (polygon.size() < 4) {
                            if (showMessages) {
                                JOptionPane.showMessageDialog(parentComponent, "多边形坐标数量不足,至少需要三个点",
                                    "错误", JOptionPane.ERROR_MESSAGE);
                            }
                            return null;
                        }
                        double safetyDistance = Double.parseDouble(safetyMarginStr);
                        ObstaclePathPlanner pathPlanner = new ObstaclePathPlanner(
                            polygon, widthMeters, mode, obstacleList, safetyDistance);
                        List<Lunjingguihua.PathSegment> segments = pathPlanner.generate();
                        generated = Lunjingguihua.formatPathSegments(segments);
                    }
                    // 传入参数:boundary(A), obstacles(B), plannerWidth(C), safetyMarginStr(D)
                    // 注意:YixinglujingHaveObstacel.planPath 返回 String
                    generated = YixinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
                } else {
                    // 无法判断地块类型,使用原来的方法作为后备
                    // 无法判断地块类型,默认按凸形处理或提示
                    if (showMessages) {
                        JOptionPane.showMessageDialog(parentComponent, "无法判断地块类型,使用默认路径生成方法",
                        JOptionPane.showMessageDialog(parentComponent, "无法判断地块类型,尝试按凸形地块处理",
                            "提示", JOptionPane.WARNING_MESSAGE);
                    }
                    List<Coordinate> polygon = Lunjingguihua.parseCoordinates(boundary);
                    if (polygon.size() < 4) {
                        if (showMessages) {
                            JOptionPane.showMessageDialog(parentComponent, "多边形坐标数量不足,至少需要三个点",
                                "错误", JOptionPane.ERROR_MESSAGE);
                        }
                        return null;
                    }
                    double safetyDistance = Double.parseDouble(safetyMarginStr);
                    ObstaclePathPlanner pathPlanner = new ObstaclePathPlanner(
                        polygon, widthMeters, mode, obstacleList, safetyDistance);
                    List<Lunjingguihua.PathSegment> segments = pathPlanner.generate();
                    generated = Lunjingguihua.formatPathSegments(segments);
                    List<AoxinglujingHaveObstacel.PathSegment> segments =
                        AoxinglujingHaveObstacel.planPath(boundary, obstacles, plannerWidth, safetyMarginStr);
                    generated = formatAoxingHaveObstaclePathSegments(segments);
                }
            }