张世豪
2025-12-01 d709e6dad60398fd599900cf781d0dd1e8c37c1c
src/zhangaiwu/AddDikuai.java
ÎļþÃû´Ó src/dikuai/AddDikuai.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package dikuai;
package zhangaiwu;
import javax.swing.*;
@@ -14,11 +14,15 @@
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import bianjie.jisuanmianjie;
import dikuai.Dikuai;
import dikuai.Dikuaiguanli;
import gecaoji.Device;
import bianjie.bianjieguihua2;
import lujing.Lunjingguihua;
import ui.UIConfig;
import zhuye.MowerLocationData;
import zhuye.Shouye;
@@ -62,6 +66,7 @@
    private JButton nextButton;
    private JButton createButton;
    private JLabel boundaryCountLabel;
    private JPanel obstacleListContainer;
    
    // åœ°å—数据
    private Map<String, String> dikuaiData = new HashMap<>();
@@ -219,6 +224,10 @@
        formGroup.add(Box.createRigidArea(new Dimension(0, 8)));
        formGroup.add(hintLabel);
        
    formGroup.add(Box.createRigidArea(new Dimension(0, 20)));
    JPanel obstacleSection = createObstacleSummarySection();
    formGroup.add(obstacleSection);
        stepPanel.add(formGroup);
        stepPanel.add(Box.createVerticalGlue());
@@ -227,6 +236,256 @@
        return stepPanel;
    }
    
    private JPanel createObstacleSummarySection() {
        JPanel section = new JPanel();
        section.setLayout(new BoxLayout(section, BoxLayout.Y_AXIS));
        section.setOpaque(false);
        section.setAlignmentX(Component.LEFT_ALIGNMENT);
        JLabel title = new JLabel("已有障碍物");
        title.setFont(new Font("微软雅黑", Font.BOLD, 16));
        title.setForeground(TEXT_COLOR);
        title.setAlignmentX(Component.LEFT_ALIGNMENT);
        section.add(title);
        section.add(Box.createRigidArea(new Dimension(0, 8)));
        obstacleListContainer = new JPanel();
        obstacleListContainer.setLayout(new BoxLayout(obstacleListContainer, BoxLayout.Y_AXIS));
        obstacleListContainer.setOpaque(false);
        obstacleListContainer.setAlignmentX(Component.LEFT_ALIGNMENT);
        section.add(obstacleListContainer);
        updateObstacleSummary();
        return section;
    }
    private void updateObstacleSummary() {
        if (obstacleListContainer == null) {
            return;
        }
        obstacleListContainer.removeAll();
        List<ObstacleSummary> summaries = loadExistingObstacles();
        if (summaries.isEmpty()) {
            JLabel emptyLabel = new JLabel("暂无障碍物");
            emptyLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
            emptyLabel.setForeground(LIGHT_TEXT);
            emptyLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
            obstacleListContainer.add(emptyLabel);
        } else {
            int index = 1;
            for (ObstacleSummary summary : summaries) {
                JPanel row = new JPanel(new BorderLayout());
                row.setOpaque(false);
                row.setAlignmentX(Component.LEFT_ALIGNMENT);
                row.setBorder(BorderFactory.createEmptyBorder(6, 0, 6, 0));
                String labelText = String.format(Locale.CHINA, "%02d. %s", index++, summary.getName());
                JLabel nameLabel = new JLabel(labelText);
                nameLabel.setFont(new Font("微软雅黑", Font.BOLD, 14));
                nameLabel.setForeground(TEXT_COLOR);
                JLabel coordLabel = new JLabel(summary.getDisplayCoords());
                coordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
                coordLabel.setForeground(LIGHT_TEXT);
                coordLabel.setToolTipText(summary.getFullCoords());
                row.add(nameLabel, BorderLayout.WEST);
                row.add(coordLabel, BorderLayout.CENTER);
                obstacleListContainer.add(row);
            }
        }
        obstacleListContainer.revalidate();
        obstacleListContainer.repaint();
    }
    private List<ObstacleSummary> loadExistingObstacles() {
        List<ObstacleSummary> summaries = new ArrayList<>();
        String landNumber = getPendingLandNumber();
        String raw = null;
        if (landNumber != null) {
            Dikuai dikuai = Dikuai.getDikuai(landNumber);
            if (dikuai != null) {
                raw = normalizeCoordinateValue(dikuai.getObstacleCoordinates());
            }
        }
        if (raw == null) {
            raw = normalizeCoordinateValue(dikuaiData.get("obstacleCoordinates"));
        }
        if (!isMeaningfulValue(raw)) {
            return summaries;
        }
        String normalized = stripInlineComment(raw);
        if (normalized.isEmpty()) {
            return summaries;
        }
        List<String> entries = splitObstacleEntries(normalized);
        int defaultIndex = 1;
        for (String entry : entries) {
            String trimmedEntry = stripInlineComment(entry);
            if (trimmedEntry.isEmpty()) {
                continue;
            }
            String nameToken = null;
            String shapeToken = null;
            String coordsSection = trimmedEntry;
            if (trimmedEntry.contains("::")) {
                String[] parts = trimmedEntry.split("::", 3);
                if (parts.length == 3) {
                    nameToken = parts[0].trim();
                    shapeToken = parts[1].trim();
                    coordsSection = parts[2].trim();
                }
            } else if (trimmedEntry.contains("@")) {
                String[] parts = trimmedEntry.split("@", 3);
                if (parts.length == 3) {
                    nameToken = parts[0].trim();
                    shapeToken = parts[1].trim();
                    coordsSection = parts[2].trim();
                } else if (parts.length == 2) {
                    shapeToken = parts[0].trim();
                    coordsSection = parts[1].trim();
                }
            } else if (trimmedEntry.contains(":")) {
                String[] parts = trimmedEntry.split(":", 3);
                if (parts.length == 3) {
                    nameToken = parts[0].trim();
                    shapeToken = parts[1].trim();
                    coordsSection = parts[2].trim();
                } else if (parts.length == 2) {
                    if (looksLikeShapeToken(parts[0])) {
                        shapeToken = parts[0].trim();
                        coordsSection = parts[1].trim();
                    } else {
                        nameToken = parts[0].trim();
                        coordsSection = parts[1].trim();
                    }
                }
            }
            String sanitizedCoords = sanitizeCoordinateString(coordsSection);
            if (!isMeaningfulValue(sanitizedCoords)) {
                continue;
            }
            String resolvedName;
            if (nameToken != null && !nameToken.isEmpty()) {
                resolvedName = nameToken;
            } else {
                resolvedName = "障碍物" + defaultIndex++;
            }
            String displayCoords = truncateCoordinateForDisplay(sanitizedCoords, 12);
            summaries.add(new ObstacleSummary(resolvedName, sanitizedCoords, displayCoords));
        }
        return summaries;
    }
    private List<String> splitObstacleEntries(String data) {
        List<String> entries = new ArrayList<>();
        if (data.indexOf('|') >= 0) {
            String[] parts = data.split("\\|");
            for (String part : parts) {
                if (part != null && !part.trim().isEmpty()) {
                    entries.add(part.trim());
                }
            }
        } else if (data.contains("\n")) {
            String[] lines = data.split("\r?\n");
            for (String line : lines) {
                if (line != null && !line.trim().isEmpty()) {
                    entries.add(line.trim());
                }
            }
        } else {
            entries.add(data);
        }
        return entries;
    }
    private String stripInlineComment(String text) {
        if (text == null) {
            return "";
        }
        int hashIndex = text.indexOf('#');
        if (hashIndex >= 0) {
            return text.substring(0, hashIndex).trim();
        }
        return text.trim();
    }
    private boolean looksLikeShapeToken(String token) {
        if (token == null) {
            return false;
        }
        String normalized = token.trim().toLowerCase(Locale.ROOT);
        return "circle".equals(normalized)
                || "polygon".equals(normalized)
                || "圆形".equals(normalized)
                || "多边形".equals(normalized)
                || "0".equals(normalized)
                || "1".equals(normalized);
    }
    private String sanitizeCoordinateString(String value) {
        if (value == null) {
            return "";
        }
        String stripped = stripInlineComment(value);
        if (stripped.isEmpty()) {
            return "";
        }
        return stripped.replace("\r", "").replace("\n", "").replace("\t", "").replace(" ", "");
    }
    private String truncateCoordinateForDisplay(String coords, int maxLength) {
        if (coords == null) {
            return "";
        }
        String trimmed = coords.trim();
        if (trimmed.length() <= maxLength) {
            return trimmed;
        }
        if (maxLength <= 3) {
            return trimmed.substring(0, maxLength);
        }
        return trimmed.substring(0, maxLength - 3) + "...";
    }
    private static final class ObstacleSummary {
        private final String name;
        private final String fullCoords;
        private final String displayCoords;
        ObstacleSummary(String name, String fullCoords, String displayCoords) {
            this.name = name;
            this.fullCoords = fullCoords;
            this.displayCoords = displayCoords;
        }
        public String getName() {
            return name;
        }
        public String getFullCoords() {
            return fullCoords;
        }
        public String getDisplayCoords() {
            return displayCoords;
        }
    }
    private JPanel createStep2Panel() {
        JPanel stepPanel = new JPanel();
        stepPanel.setLayout(new BoxLayout(stepPanel, BoxLayout.Y_AXIS));
@@ -675,9 +934,89 @@
            showStep(2);
            return;
        }
        dikuaiData.put("mowingPattern", (String) mowingPatternCombo.getSelectedItem());
        dikuaiData.put("mowingWidth", mowingWidthSpinner.getValue().toString());
        JOptionPane.showMessageDialog(this, "已根据当前设置生成割草路径", "成功", JOptionPane.INFORMATION_MESSAGE);
        Dikuai dikuai = getOrCreatePendingDikuai();
        String boundaryCoords = null;
        if (dikuai != null) {
            boundaryCoords = normalizeCoordinateValue(dikuai.getBoundaryCoordinates());
        }
        if (boundaryCoords == null) {
            boundaryCoords = normalizeCoordinateValue(dikuaiData.get("boundaryCoordinates"));
        }
        if (boundaryCoords == null) {
            JOptionPane.showMessageDialog(this, "未找到有效的地块边界坐标,无法生成路径", "提示", JOptionPane.WARNING_MESSAGE);
            if (createButton != null) {
                createButton.setEnabled(false);
            }
            return;
        }
        String obstacleCoords = null;
        if (dikuai != null) {
            obstacleCoords = normalizeCoordinateValue(dikuai.getObstacleCoordinates());
        }
        if (obstacleCoords == null) {
            obstacleCoords = normalizeCoordinateValue(dikuaiData.get("obstacleCoordinates"));
        }
        String patternDisplay = (String) mowingPatternCombo.getSelectedItem();
        dikuaiData.put("mowingPattern", patternDisplay);
        Object widthObj = mowingWidthSpinner.getValue();
        if (!(widthObj instanceof Number)) {
            JOptionPane.showMessageDialog(this, "割草宽度输入无效", "提示", JOptionPane.WARNING_MESSAGE);
            if (createButton != null) {
                createButton.setEnabled(false);
            }
            return;
        }
        double widthCm = ((Number) widthObj).doubleValue();
        if (widthCm <= 0) {
            JOptionPane.showMessageDialog(this, "割草宽度必须大于0", "提示", JOptionPane.WARNING_MESSAGE);
            if (createButton != null) {
                createButton.setEnabled(false);
            }
            return;
        }
        dikuaiData.put("mowingWidth", widthObj.toString());
        String widthMeters = String.format(Locale.US, "%.2f", widthCm / 100.0);
        String plannerMode = resolvePlannerMode(patternDisplay);
        try {
            List<Lunjingguihua.PathSegment> segments = Lunjingguihua.generatePathSegments(
                boundaryCoords,
                obstacleCoords,
                widthMeters,
                plannerMode
            );
            String plannedPath = Lunjingguihua.formatPathSegments(segments);
            if (!isMeaningfulValue(plannedPath)) {
                JOptionPane.showMessageDialog(this, "生成割草路径失败: ç”Ÿæˆç»“果为空", "错误", JOptionPane.ERROR_MESSAGE);
                if (createButton != null) {
                    createButton.setEnabled(false);
                }
                return;
            }
            dikuaiData.put("plannedPath", plannedPath);
            if (createButton != null) {
                createButton.setEnabled(true);
            }
            JOptionPane.showMessageDialog(this,
                "已根据当前设置生成割草路径,共生成 " + segments.size() + " æ®µã€‚",
                "成功",
                JOptionPane.INFORMATION_MESSAGE);
        } catch (IllegalArgumentException ex) {
            JOptionPane.showMessageDialog(this, "生成割草路径失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
            if (createButton != null) {
                createButton.setEnabled(false);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            JOptionPane.showMessageDialog(this, "生成割草路径时发生异常: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
            if (createButton != null) {
                createButton.setEnabled(false);
            }
        }
    }
    
    private JButton createPrimaryButton(String text, int fontSize) {
@@ -732,6 +1071,7 @@
        nextButton = createPrimaryButton("下一步", 16);
        createButton = createPrimaryButton("保存", 16);
        createButton.setVisible(false);
    createButton.setEnabled(false);
        buttonPanel.add(prevButton);
        buttonPanel.add(Box.createHorizontalGlue());
@@ -881,6 +1221,36 @@
        }
    }
    
    private boolean hasGeneratedPath() {
        return isMeaningfulValue(dikuaiData.get("plannedPath"));
    }
    private String normalizeCoordinateValue(String value) {
        return isMeaningfulValue(value) ? value.trim() : null;
    }
    private boolean isMeaningfulValue(String value) {
        if (value == null) {
            return false;
        }
        String trimmed = value.trim();
        return !trimmed.isEmpty() && !"-1".equals(trimmed);
    }
    private String resolvePlannerMode(String patternDisplay) {
        if (patternDisplay == null) {
            return "parallel";
        }
        String trimmed = patternDisplay.trim();
        if (trimmed.isEmpty()) {
            return "parallel";
        }
        if ("螺旋式".equals(trimmed) || "spiral".equalsIgnoreCase(trimmed) || "1".equals(trimmed)) {
            return "spiral";
        }
        return "parallel";
    }
    private void setupEventHandlers() {
        // ä¸Šä¸€æ­¥æŒ‰é’®
        prevButton.addActionListener(e -> {
@@ -922,6 +1292,10 @@
        currentStep = step;
        cardLayout.show(stepsPanel, "step" + step);
        
        if (step == 1) {
            updateObstacleSummary();
        }
        // æ›´æ–°æŒ‰é’®çŠ¶æ€
        updateButtonState(step);
    }
@@ -932,9 +1306,11 @@
        if (step < 3) {
            nextButton.setVisible(true);
            createButton.setVisible(false);
            createButton.setEnabled(false);
        } else {
            nextButton.setVisible(false);
            createButton.setVisible(true);
            createButton.setEnabled(hasGeneratedPath());
        }
        Container parent = prevButton.getParent();
@@ -977,6 +1353,10 @@
            case 3:
                dikuaiData.put("mowingPattern", (String) mowingPatternCombo.getSelectedItem());
                dikuaiData.put("mowingWidth", mowingWidthSpinner.getValue().toString());
                if (!hasGeneratedPath()) {
                    JOptionPane.showMessageDialog(this, "请先生成割草路径", "提示", JOptionPane.WARNING_MESSAGE);
                    return false;
                }
                break;
        }
        return true;
@@ -1271,6 +1651,11 @@
            dikuai.setMowingWidth(dikuaiData.get("mowingWidth"));
        }
        String plannedPath = dikuaiData.get("plannedPath");
        if (isMeaningfulValue(plannedPath)) {
            dikuai.setPlannedPath(plannedPath);
        }
        Dikuai.putDikuai(landNumber, dikuai);
        Dikuai.saveToProperties();