| | |
| | | package dikuai; |
| | | |
| | | import javax.swing.*; |
| | | import java.awt.*; |
| | | import java.awt.event.*; |
| | |
| | | import java.util.Objects; |
| | | import java.util.Properties; |
| | | import java.util.Locale; |
| | | |
| | | import lujing.Lunjingguihua; |
| | | import lujing.MowingPathGenerationPage; |
| | | import publicway.Fuzhibutton; |
| | | import publicway.Lookbutton; |
| | |
| | | public boolean savePlannedPath(Dikuai dikuai, String value) { |
| | | return saveFieldAndRefresh(dikuai, "plannedPath", value); |
| | | } |
| | | |
| | | @Override |
| | | public boolean saveMowingSafetyDistance(Dikuai dikuai, String value) { |
| | | return saveFieldAndRefresh(dikuai, "mowingSafetyDistance", value); |
| | | } |
| | | }; |
| | | |
| | | // 显示路径规划页面 |
| | |
| | | } |
| | | } |
| | | String modeValue = sanitizeValueOrNull(dikuai.getMowingPattern()); |
| | | String initialGenerated = attemptMowingPathPreview( |
| | | boundaryValue, |
| | | obstacleValue, |
| | | widthValue, |
| | | modeValue, |
| | | this, |
| | | false |
| | | ); |
| | | // 不再预先生成路径,由路径规划页面处理 |
| | | String initialGenerated = null; |
| | | showMowingPathDialog(dikuai, baseStationValue, boundaryValue, obstacleValue, widthValue, modeValue, initialGenerated); |
| | | } |
| | | |
| | |
| | | public boolean savePlannedPath(Dikuai dikuai, String value) { |
| | | return saveFieldAndRefresh(dikuai, "plannedPath", value); |
| | | } |
| | | |
| | | @Override |
| | | public boolean saveMowingSafetyDistance(Dikuai dikuai, String value) { |
| | | return saveFieldAndRefresh(dikuai, "mowingSafetyDistance", value); |
| | | } |
| | | }; |
| | | |
| | | // 使用新的独立页面类 |
| | |
| | | dialog.setVisible(true); |
| | | } |
| | | |
| | | private String attemptMowingPathPreview( |
| | | String boundaryInput, |
| | | String obstacleInput, |
| | | String widthCmInput, |
| | | String modeInput, |
| | | Component parentComponent, |
| | | boolean showMessages) { |
| | | String boundary = sanitizeValueOrNull(boundaryInput); |
| | | if (boundary == null) { |
| | | if (showMessages) { |
| | | JOptionPane.showMessageDialog(parentComponent, "当前地块未设置边界坐标,无法生成路径", "提示", JOptionPane.WARNING_MESSAGE); |
| | | } |
| | | return null; |
| | | } |
| | | String rawWidth = widthCmInput != null ? widthCmInput.trim() : ""; |
| | | String widthStr = sanitizeWidthString(widthCmInput); |
| | | if (widthStr == null) { |
| | | if (showMessages) { |
| | | String message = rawWidth.isEmpty() ? "请先设置割草宽度(厘米)" : "割草宽度格式不正确"; |
| | | JOptionPane.showMessageDialog(parentComponent, message, "提示", JOptionPane.WARNING_MESSAGE); |
| | | } |
| | | return null; |
| | | } |
| | | double widthCm; |
| | | try { |
| | | widthCm = Double.parseDouble(widthStr); |
| | | } catch (NumberFormatException ex) { |
| | | if (showMessages) { |
| | | JOptionPane.showMessageDialog(parentComponent, "割草宽度格式不正确", "提示", JOptionPane.WARNING_MESSAGE); |
| | | } |
| | | return null; |
| | | } |
| | | if (widthCm <= 0) { |
| | | if (showMessages) { |
| | | JOptionPane.showMessageDialog(parentComponent, "割草宽度必须大于0", "提示", JOptionPane.WARNING_MESSAGE); |
| | | } |
| | | return null; |
| | | } |
| | | double widthMeters = widthCm / 100.0d; |
| | | String plannerWidth = BigDecimal.valueOf(widthMeters) |
| | | .setScale(3, RoundingMode.HALF_UP) |
| | | .stripTrailingZeros() |
| | | .toPlainString(); |
| | | String obstacles = sanitizeValueOrNull(obstacleInput); |
| | | if (obstacles != null) { |
| | | obstacles = obstacles.replace("\r\n", " ").replace('\r', ' ').replace('\n', ' '); |
| | | } |
| | | String mode = normalizeExistingMowingPattern(modeInput); |
| | | try { |
| | | String generated = Lunjingguihua.generatePathFromStrings(boundary, obstacles, plannerWidth, mode); |
| | | String trimmed = generated != null ? generated.trim() : ""; |
| | | if (trimmed.isEmpty()) { |
| | | if (showMessages) { |
| | | JOptionPane.showMessageDialog(parentComponent, "未生成有效的割草路径,请检查地块数据", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | } |
| | | return null; |
| | | } |
| | | if (showMessages) { |
| | | JOptionPane.showMessageDialog(parentComponent, "割草路径已生成", "成功", JOptionPane.INFORMATION_MESSAGE); |
| | | } |
| | | return trimmed; |
| | | } catch (IllegalArgumentException ex) { |
| | | if (showMessages) { |
| | | JOptionPane.showMessageDialog(parentComponent, "生成割草路径失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); |
| | | } |
| | | } catch (Exception ex) { |
| | | if (showMessages) { |
| | | JOptionPane.showMessageDialog(parentComponent, "生成割草路径时发生异常: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | /** |
| | | * 尝试生成割草路径预览 |
| | | * 用于在对话框中实时预览或生成路径,包含参数校验和错误提示逻辑 |
| | | * |
| | | * @param boundaryInput 地块边界坐标字符串 |
| | | * @param obstacleInput 障碍物坐标字符串 |
| | | * @param widthCmInput 割草宽度(厘米) |
| | | * @param modeInput 割草模式(平行/螺旋) |
| | | * @param parentComponent 用于显示提示框的父组件 |
| | | * @param showMessages 是否显示成功/失败的提示框 |
| | | * @return 生成的路径字符串,如果生成失败或校验未通过则返回 null |
| | | */ |
| | | |
| | | |
| | | private JTextArea createInfoTextArea(String text, boolean editable, int rows) { |
| | | JTextArea area = new JTextArea(text); |
| | |
| | | return false; |
| | | } |
| | | |
| | | // 同时保存到 dikuai.properties |
| | | saveFieldAndRefresh(dikuai, "obstacleCoordinates", obstaclePayload); |
| | | |
| | | obstacleSummaryCache = loadObstacleSummaries(); |
| | | boolean isCurrent = landNumber.equals(currentWorkLandNumber); |
| | | loadDikuaiData(); |