| ÎļþÃû´Ó src/dikuai/AddDikuai.java ÐÞ¸Ä |
| | |
| | | package dikuai; |
| | | package zhangaiwu; |
| | | |
| | | import javax.swing.*; |
| | | |
| | |
| | | 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; |
| | |
| | | private JButton nextButton; |
| | | private JButton createButton; |
| | | private JLabel boundaryCountLabel; |
| | | private JPanel obstacleListContainer; |
| | | |
| | | // å°åæ°æ® |
| | | private Map<String, String> dikuaiData = new HashMap<>(); |
| | |
| | | formGroup.add(areaNameField); |
| | | 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()); |
| | |
| | | |
| | | 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(); |
| | |
| | | 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) { |
| | |
| | | nextButton = createPrimaryButton("ä¸ä¸æ¥", 16); |
| | | createButton = createPrimaryButton("ä¿å", 16); |
| | | createButton.setVisible(false); |
| | | createButton.setEnabled(false); |
| | | |
| | | buttonPanel.add(prevButton); |
| | | buttonPanel.add(Box.createHorizontalGlue()); |
| | |
| | | boundaryCountLabel.setVisible(false); |
| | | } |
| | | } |
| | | |
| | | 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() { |
| | | // ä¸ä¸æ¥æé® |
| | |
| | | currentStep = step; |
| | | cardLayout.show(stepsPanel, "step" + step); |
| | | |
| | | if (step == 1) { |
| | | updateObstacleSummary(); |
| | | } |
| | | |
| | | // æ´æ°æé®ç¶æ |
| | | updateButtonState(step); |
| | | } |
| | |
| | | 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(); |
| | |
| | | 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; |
| | |
| | | dikuai.setMowingWidth(dikuaiData.get("mowingWidth")); |
| | | } |
| | | |
| | | String plannedPath = dikuaiData.get("plannedPath"); |
| | | if (isMeaningfulValue(plannedPath)) { |
| | | dikuai.setPlannedPath(plannedPath); |
| | | } |
| | | |
| | | Dikuai.putDikuai(landNumber, dikuai); |
| | | Dikuai.saveToProperties(); |
| | | |