| | |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | |
| | | import zhangaiwu.AddDikuai; |
| | | import zhangaiwu.Obstacledge; |
| | |
| | | private ImageIcon boundaryVisibleIcon; |
| | | private ImageIcon boundaryHiddenIcon; |
| | | private static final int BOUNDARY_TOGGLE_ICON_SIZE = 48; |
| | | private Map<String, ObstacleSummary> obstacleSummaryCache = Collections.emptyMap(); |
| | | |
| | | public Dikuaiguanli(String landNumber) { |
| | | latestInstance = this; |
| | |
| | | cardsPanel.removeAll(); |
| | | |
| | | Map<String, Dikuai> allDikuai = Dikuai.getAllDikuai(); |
| | | |
| | | |
| | | if (allDikuai.isEmpty()) { |
| | | obstacleSummaryCache = Collections.emptyMap(); |
| | | // 显示空状态 |
| | | JPanel emptyPanel = createEmptyStatePanel(); |
| | | cardsPanel.add(emptyPanel); |
| | | setCurrentWorkLand(null, null); |
| | | } else { |
| | | obstacleSummaryCache = loadObstacleSummaries(); |
| | | if (allDikuai.size() == 1) { |
| | | Dikuai onlyDikuai = allDikuai.values().iterator().next(); |
| | | setCurrentWorklandIfNeeded(onlyDikuai); |
| | |
| | | contentPanel.add(boundaryPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | |
| | | // 障碍物坐标(带新增和查看按钮) |
| | | JPanel obstaclePanel = createCardInfoItemWithButton("障碍物坐标:", |
| | | getTruncatedValue(dikuai.getObstacleCoordinates(), 12, "未设置"), |
| | | ObstacleSummary obstacleSummary = getObstacleSummaryFromCache(dikuai.getLandNumber()); |
| | | JPanel obstaclePanel = createCardInfoItemWithButton("障碍物:", |
| | | obstacleSummary.buildDisplayValue(), |
| | | "新增", |
| | | e -> addNewObstacle(dikuai)); |
| | | setInfoItemTooltip(obstaclePanel, dikuai.getObstacleCoordinates()); |
| | | setInfoItemTooltip(obstaclePanel, obstacleSummary.buildTooltip()); |
| | | contentPanel.add(obstaclePanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | |
| | |
| | | } |
| | | MapRenderer renderer = shouye.getMapRenderer(); |
| | | if (renderer != null) { |
| | | renderer.applyLandMetadata(dikuai); |
| | | String boundary = (dikuai != null) ? dikuai.getBoundaryCoordinates() : null; |
| | | String plannedPath = (dikuai != null) ? dikuai.getPlannedPath() : null; |
| | | String obstacles = (dikuai != null) ? dikuai.getObstacleCoordinates() : null; |
| | | List<Obstacledge.Obstacle> configuredObstacles = (landNumber != null) ? loadObstaclesFromConfig(landNumber) : null; |
| | | renderer.setCurrentBoundary(boundary, landNumber, landNumber == null ? null : landName); |
| | | renderer.setCurrentPlannedPath(plannedPath); |
| | | renderer.setCurrentObstacles(obstacles, landNumber); |
| | | if (configuredObstacles != null) { |
| | | renderer.setCurrentObstacles(configuredObstacles, landNumber); |
| | | } else { |
| | | renderer.setCurrentObstacles(obstacles, landNumber); |
| | | } |
| | | boolean showBoundaryPoints = landNumber != null && boundaryPointVisibility.getOrDefault(landNumber, false); |
| | | renderer.setBoundaryPointsVisible(showBoundaryPoints); |
| | | } |
| | | shouye.refreshMowingIndicators(); |
| | | } |
| | | } |
| | | |
| | |
| | | loadDikuaiData(); |
| | | } |
| | | |
| | | private Map<String, ObstacleSummary> loadObstacleSummaries() { |
| | | Map<String, ObstacleSummary> summaries = new HashMap<>(); |
| | | try { |
| | | File configFile = new File("Obstacledge.properties"); |
| | | if (!configFile.exists()) { |
| | | return summaries; |
| | | } |
| | | Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager(); |
| | | if (!manager.loadFromFile(configFile.getAbsolutePath())) { |
| | | return summaries; |
| | | } |
| | | for (Obstacledge.Plot plot : manager.getPlots()) { |
| | | if (plot == null) { |
| | | continue; |
| | | } |
| | | String plotId = plot.getPlotId(); |
| | | if (plotId == null || plotId.trim().isEmpty()) { |
| | | continue; |
| | | } |
| | | List<String> names = new ArrayList<>(); |
| | | for (Obstacledge.Obstacle obstacle : plot.getObstacles()) { |
| | | if (obstacle == null) { |
| | | continue; |
| | | } |
| | | String name = obstacle.getObstacleName(); |
| | | if (name == null) { |
| | | continue; |
| | | } |
| | | String trimmed = name.trim(); |
| | | if (!trimmed.isEmpty()) { |
| | | names.add(trimmed); |
| | | } |
| | | } |
| | | summaries.put(plotId.trim(), ObstacleSummary.of(names)); |
| | | } |
| | | } catch (Exception ex) { |
| | | System.err.println("读取障碍物配置失败: " + ex.getMessage()); |
| | | } |
| | | return summaries; |
| | | } |
| | | |
| | | private static List<Obstacledge.Obstacle> loadObstaclesFromConfig(String landNumber) { |
| | | if (landNumber == null || landNumber.trim().isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | try { |
| | | File configFile = new File("Obstacledge.properties"); |
| | | if (!configFile.exists()) { |
| | | return null; |
| | | } |
| | | Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager(); |
| | | if (!manager.loadFromFile(configFile.getAbsolutePath())) { |
| | | return null; |
| | | } |
| | | Obstacledge.Plot plot = manager.getPlotById(landNumber.trim()); |
| | | if (plot == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<Obstacledge.Obstacle> obstacles = plot.getObstacles(); |
| | | if (obstacles == null || obstacles.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return new ArrayList<>(obstacles); |
| | | } catch (Exception ex) { |
| | | System.err.println("读取障碍物配置失败: " + ex.getMessage()); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private ObstacleSummary getObstacleSummaryFromCache(String landNumber) { |
| | | if (landNumber == null || landNumber.trim().isEmpty()) { |
| | | return ObstacleSummary.empty(); |
| | | } |
| | | if (obstacleSummaryCache == null || obstacleSummaryCache.isEmpty()) { |
| | | return ObstacleSummary.empty(); |
| | | } |
| | | ObstacleSummary summary = obstacleSummaryCache.get(landNumber.trim()); |
| | | return summary != null ? summary : ObstacleSummary.empty(); |
| | | } |
| | | |
| | | private List<String> loadObstacleNamesForLand(String landNumber) { |
| | | List<String> names = new ArrayList<>(); |
| | | if (landNumber == null || landNumber.trim().isEmpty()) { |
| | | return names; |
| | | } |
| | | try { |
| | | File configFile = new File("Obstacledge.properties"); |
| | | if (!configFile.exists()) { |
| | | return names; |
| | | } |
| | | Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager(); |
| | | if (!manager.loadFromFile(configFile.getAbsolutePath())) { |
| | | return names; |
| | | } |
| | | Obstacledge.Plot plot = manager.getPlotById(landNumber.trim()); |
| | | if (plot == null) { |
| | | return names; |
| | | } |
| | | for (Obstacledge.Obstacle obstacle : plot.getObstacles()) { |
| | | if (obstacle == null) { |
| | | continue; |
| | | } |
| | | String name = obstacle.getObstacleName(); |
| | | if (name == null) { |
| | | continue; |
| | | } |
| | | String trimmed = name.trim(); |
| | | if (!trimmed.isEmpty() && !names.contains(trimmed)) { |
| | | names.add(trimmed); |
| | | } |
| | | } |
| | | } catch (Exception ex) { |
| | | System.err.println("读取障碍物配置失败: " + ex.getMessage()); |
| | | ObstacleSummary cached = getObstacleSummaryFromCache(landNumber); |
| | | if (!cached.isEmpty()) { |
| | | names.addAll(cached.copyNames()); |
| | | return names; |
| | | } |
| | | Map<String, ObstacleSummary> latest = loadObstacleSummaries(); |
| | | if (!latest.isEmpty()) { |
| | | obstacleSummaryCache = latest; |
| | | } |
| | | ObstacleSummary refreshed = getObstacleSummaryFromCache(landNumber); |
| | | if (!refreshed.isEmpty()) { |
| | | names.addAll(refreshed.copyNames()); |
| | | } |
| | | return names; |
| | | } |
| | |
| | | } |
| | | boundaryPointVisibility.put(landNumber, visible); |
| | | } |
| | | |
| | | private static final class ObstacleSummary { |
| | | private static final ObstacleSummary EMPTY = new ObstacleSummary(Collections.emptyList()); |
| | | private final List<String> names; |
| | | |
| | | private ObstacleSummary(List<String> names) { |
| | | this.names = names; |
| | | } |
| | | |
| | | static ObstacleSummary of(List<String> originalNames) { |
| | | if (originalNames == null || originalNames.isEmpty()) { |
| | | return empty(); |
| | | } |
| | | List<String> cleaned = new ArrayList<>(); |
| | | for (String name : originalNames) { |
| | | if (name == null) { |
| | | continue; |
| | | } |
| | | String trimmed = name.trim(); |
| | | if (trimmed.isEmpty()) { |
| | | continue; |
| | | } |
| | | boolean duplicated = false; |
| | | for (String existing : cleaned) { |
| | | if (existing.equalsIgnoreCase(trimmed)) { |
| | | duplicated = true; |
| | | break; |
| | | } |
| | | } |
| | | if (!duplicated) { |
| | | cleaned.add(trimmed); |
| | | } |
| | | } |
| | | if (cleaned.isEmpty()) { |
| | | return empty(); |
| | | } |
| | | cleaned.sort(String::compareToIgnoreCase); |
| | | return new ObstacleSummary(Collections.unmodifiableList(cleaned)); |
| | | } |
| | | |
| | | static ObstacleSummary empty() { |
| | | return EMPTY; |
| | | } |
| | | |
| | | boolean isEmpty() { |
| | | return names.isEmpty(); |
| | | } |
| | | |
| | | int count() { |
| | | return names.size(); |
| | | } |
| | | |
| | | String buildDisplayValue() { |
| | | return count() > 0 ? String.format("障碍物%d个", count()) : "暂无障碍物"; |
| | | } |
| | | |
| | | String buildTooltip() { |
| | | return count() > 0 ? String.join(",", names) : "暂无障碍物"; |
| | | } |
| | | |
| | | List<String> copyNames() { |
| | | return new ArrayList<>(names); |
| | | } |
| | | } |
| | | } |