张世豪
3 天以前 f66407df7e4971a7a85e4b281cc199a05ec84987
优化了功能
已修改4个文件
已添加2个文件
536 ■■■■■ 文件已修改
.classpath 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dikuai.properties 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
lib/MQTT-1.0-SNAPSHOT.jar 补丁 | 查看 | 原始文档 | blame | 历史
set.properties 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/udpdell/Mqttserver.java 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/zhangaiwu/AddDikuai.java 380 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.classpath
@@ -10,5 +10,6 @@
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/jts-core-1.19.0.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/slf4j-api-1.7.30.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/slf4j-simple-1.7.30.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/MQTT-1.0-SNAPSHOT.jar"/>
    <classpathentry kind="output" path="bin"/>
</classpath>
dikuai.properties
@@ -1,2 +1,2 @@
#Dikuai Properties
#Tue Dec 23 13:52:20 CST 2025
#Tue Dec 23 16:12:08 CST 2025
lib/MQTT-1.0-SNAPSHOT.jar
Binary files differ
set.properties
@@ -1,5 +1,5 @@
#Mower Configuration Properties - Updated
#Tue Dec 23 14:54:31 CST 2025
#Tue Dec 23 16:12:22 CST 2025
appVersion=-1
boundaryLengthVisible=false
currentWorkLandNumber=-1
@@ -8,12 +8,12 @@
handheldMarkerId=1872
idleTrailDurationSeconds=60
manualBoundaryDrawingMode=false
mapScale=20.00
mapScale=15.41
measurementModeEnabled=false
mowerId=860
serialAutoConnect=true
serialBaudRate=115200
serialPortName=COM15
simCardNumber=-1
viewCenterX=0.00
viewCenterY=0.00
viewCenterX=-14.71
viewCenterY=5.21
src/udpdell/Mqttserver.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,145 @@
package udpdell;
import Util.DeviceMessageParser;
import Util.LawnMowerCommandJsonGenerator;
import Util.MowerPathMessageGenerator;
import Util.Entity.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.Arrays;
import java.util.List;
public class Mqttserver {
//    public static void main(String[] args) {
//        GPSData();//解析设备上传GPS数据示例
//        responseData();//解析设备回复的响应数据示例
//        outputData();//设备控制指令生成示例
//        //String s = generateExampleMessage();//路径规划数据生成示例
//        //System.out.println(s);
//    }
    private static void responseData(){
        // è§£æžå“åº”消息
        String responseJson = "{ \"msg_id\": \"hxzkresponse_20151105\", \"timestamp\": 1621234568300, \"device_id\": \"MOWER_001\", \"original_msg_id\": \"msg_123456793\", \"response\": { \"status\": \"success\", \"command\": \"start\", \"error_code\": 0, \"error_message\": \"\", \"additional_info\": { \"current_status\": \"running\", \"battery_level\": 84, \"current_position\": { \"lat\": \"3949.91202005,N\", \"lon\": \"11616.85440851,E\" } } } }";
        ResponseData responseData = null;
        try {
            responseData = DeviceMessageParser.parseResponseData(responseJson);
            System.out.println("原始消息ID: " + responseData.getOriginalMsgId());
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            System.err.println("解析错误: " + e.getMessage());
        }
    }
    private static void GPSData() {
        // GPS数据示例(新协议格式,包含GPS、IMU和状态数据)
        String gpsJsonStr = "{\n" +
                "  \"msg_id\": \"hxzkgps_20151101\",\n" +
                "  \"timestamp\": 1621234567890,\n" +
                "  \"device_id\": \"MOWER_001\",\n" +
                "  \"data_type\": \"gps\",\n" +
                "  \"gps_raw\": \"$GNGGA,024830.90,3949.91202005,N,11616.85440851,E,4,26,0.7,49.6405,M,-8.7435,M,0.9,409*4A,2976,28,0,0,2,0\",\n" +
                "  \"imu_data\": {\n" +
                "    \"roll\": 1.2,\n" +
                "    \"pitch\": 0.5,\n" +
                "    \"yaw\": 185.5\n" +
                "  },\n" +
                "  \"status\": {\n" +
                "    \"battery_level\": 85,\n" +
                "    \"battery_voltage\": 24.5,\n" +
                "    \"operation_mode\": \"auto\",\n" +
                "    \"motor_status\": \"running\",\n" +
                "    \"blade_status\": \"rotating\",\n" +
                "    \"blade_height\": 10,\n" +
                "    \"self_check_status\": 1,\n" +
                "    \"error_code\": 0,\n" +
                "    \"error_message\": \"\"\n" +
                "  }\n" +
                "}";
        try {
            System.out.println("=== GPS数据解析示例(新协议格式) ===");
            GPSData gpsData2 = DeviceMessageParser.parseGPSData(gpsJsonStr);
            String string2 = gpsData2.toString();
            System.out.println(string2);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            System.err.println("解析错误: " + e.getMessage());
        }
    }
    public static void outputData() {
        // ç¤ºä¾‹ï¼šä½¿ç”¨ä¾¿æ·æ–¹æ³•生成转向命令
        String json2 = LawnMowerCommandJsonGenerator.generateTurnCommandJson(
                "hxzkcontrol_20151105",
                null,
                "USER_001",
                "MOWER_001",
                180.0, // è½¬å‘180度
                0.5,   // é€Ÿåº¦0.5ç±³/秒
                10     // æŒç»­10秒
        );
        System.out.println(json2);
    }
    public static String generateExampleMessage(int value) {
        // åˆ›å»ºè¾¹ç•Œç‚¹ï¼ˆä½¿ç”¨XY坐标)
        HxzkPathMessage message = getmessage(value);
        return MowerPathMessageGenerator.toJson(message);
    }
    private static HxzkPathMessage getmessage(int value) {
        List<CoordinatePoint> boundaryPoints = Arrays.asList(
                 MowerPathMessageGenerator.createCoordinatePoint("100.5", "200.3"),
                 MowerPathMessageGenerator.createCoordinatePoint("150.8", "200.5"),
                 MowerPathMessageGenerator.createCoordinatePoint("150.9", "250.7")
        );
        // åˆ›å»ºå¯¼èˆªç‚¹ï¼ˆä½¿ç”¨XY坐标)
        List<CoordinatePoint> navigationPoints = Arrays.asList(
                 MowerPathMessageGenerator.createCoordinatePoint("100.5", "200.3"),
                 MowerPathMessageGenerator.createCoordinatePoint("150.8", "200.5"),
                 MowerPathMessageGenerator.createCoordinatePoint("150.9", "250.7")
        );
        // åˆ›å»ºåŸºå‡†ç«™æ•°æ®
        BasestationData basestationData =  MowerPathMessageGenerator.createBasestationData(
                "3949.84110064", "N",
                "11616.74587312", "E",
                45.2
        );
        // åˆ›å»ºè·¯å¾„数据
        PathData pathData =  MowerPathMessageGenerator.createPathData(
                "path_20230724_"+value,
                "WGS84_DM",
                boundaryPoints,
                navigationPoints,
                "parallel",
                boundaryPoints.size(),  // è¾¹ç•Œç‚¹æ•°é‡
                navigationPoints.size() // å¯¼èˆªç‚¹æ•°é‡
        );
        // åˆ›å»ºå®Œæ•´æ¶ˆæ¯
        HxzkPathMessage message =  MowerPathMessageGenerator.createMessage(
                "hxzkpath_"+value,
                System.currentTimeMillis(),
                "USER_"+value,
                "6528",
                "set_path",
                basestationData,
                pathData
        );
        return message;
    }
}
src/zhangaiwu/AddDikuai.java
@@ -73,7 +73,8 @@
    private JButton prevButton;
    private JButton nextButton;
    private JButton createButton;
    private JButton previewButton;
    private JButton previewButton;  // æ­¥éª¤3的预览按钮(预览割草路径)
    private JButton boundaryPreviewButton;  // æ­¥éª¤2的预览按钮(预览边界)
    private Component previewButtonSpacer;
    private JLabel boundaryCountLabel;
    private JTextArea boundaryXYTextArea;  // æ˜¾ç¤ºè¾¹ç•ŒXY坐标的文本域
@@ -260,7 +261,7 @@
        ));
        areaNameField.setAlignmentX(Component.LEFT_ALIGNMENT);
        
        // æ·»åŠ è¾“å…¥æ¡†ç„¦ç‚¹æ•ˆæžœ
        // æ·»åŠ è¾“å…¥æ¡†ç„¦ç‚¹æ•ˆæžœå’Œæ–‡æœ¬å˜åŒ–ç›‘å¬
        areaNameField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
@@ -276,6 +277,16 @@
                    BorderFactory.createLineBorder(BORDER_COLOR, 2),
                    BorderFactory.createEmptyBorder(12, 15, 12, 15)
                ));
                // æ›´æ–°ä¸‹ä¸€æ­¥æŒ‰é’®çŠ¶æ€
                updateStep1ButtonState();
            }
        });
        // æ·»åŠ æ–‡æœ¬å˜åŒ–ç›‘å¬ï¼Œå®žæ—¶æ›´æ–°æŒ‰é’®çŠ¶æ€
        areaNameField.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                updateStep1ButtonState();
            }
        });
        
@@ -654,6 +665,7 @@
        startEndDrawingBtn.setAlignmentX(Component.LEFT_ALIGNMENT);
        startEndDrawingBtn.setMaximumSize(new Dimension(400, 55));
        startEndDrawingBtn.setEnabled(false); // åˆå§‹ä¸å¯ç”¨
        startEndDrawingBtn.setBackground(MEDIUM_GRAY); // åˆå§‹ç°è‰²èƒŒæ™¯
        
        startEndDrawingBtn.addActionListener(e -> toggleDrawing());
        
@@ -773,7 +785,7 @@
                    return;
                }
                if (selectDrawingOption(optionPanel, type, true)) {
                    startEndDrawingBtn.setEnabled(true); // é€‰æ‹©åŽå¯ç”¨æŒ‰é’®
                    updateStartDrawingButtonState(); // é€‰æ‹©åŽæ›´æ–°æŒ‰é’®çŠ¶æ€
                }
            }
            
@@ -866,6 +878,8 @@
            JOptionPane.showMessageDialog(this, "边界绘制已完成", "提示", JOptionPane.INFORMATION_MESSAGE);
            showBoundaryPointSummary();
            updateBoundaryXYDisplay();
            // æ›´æ–°é¢„览和下一步按钮状态(背景颜色变绿色,可点击)
            updateStep2ButtonsAfterDrawing();
        }
    }
@@ -1515,6 +1529,11 @@
            JOptionPane.showMessageDialog(this, "无法启动预览,请稍后再试", "提示", JOptionPane.WARNING_MESSAGE);
            return;
        }
        // åœ¨æ­¥éª¤3预览时,不显示边界点圆圈
        if (shouye.getMapRenderer() != null) {
            shouye.getMapRenderer().setBoundaryPointsVisible(false);
        }
        closePreviewAndDispose();
    }
@@ -1575,14 +1594,23 @@
            @Override
            public void mouseEntered(MouseEvent e) {
                if (button.isEnabled()) {
                    button.setBackground(PRIMARY_DARK);
                    // å¦‚果按钮可用,鼠标悬停时显示深绿色
                    if (button.getBackground().equals(PRIMARY_COLOR)) {
                        button.setBackground(PRIMARY_DARK);
                    }
                }
            }
            @Override
            public void mouseExited(MouseEvent e) {
                if (button.isEnabled()) {
                    button.setBackground(PRIMARY_COLOR);
                    // å¦‚果按钮可用,鼠标离开时恢复绿色
                    if (!button.getBackground().equals(MEDIUM_GRAY)) {
                        button.setBackground(PRIMARY_COLOR);
                    }
                } else {
                    // å¦‚果按钮不可用,保持灰色
                    button.setBackground(MEDIUM_GRAY);
                }
            }
        });
@@ -1643,7 +1671,15 @@
        ));
        prevButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
        boundaryPreviewButton = createPrimaryButton("预览", 16);
        boundaryPreviewButton.setVisible(false);
        boundaryPreviewButton.setEnabled(false);
        boundaryPreviewButton.addActionListener(e -> previewBoundary());
        nextButton = createPrimaryButton("下一步", 16);
        nextButton.setBackground(MEDIUM_GRAY); // åˆå§‹ç°è‰²èƒŒæ™¯
        nextButton.setEnabled(false); // åˆå§‹ä¸å¯ç”¨
        createButton = createPrimaryButton("保存", 16);
        createButton.setVisible(false);
        createButton.setEnabled(false);
@@ -1657,6 +1693,8 @@
        buttonPanel.add(prevButton);
        buttonPanel.add(Box.createHorizontalGlue());
        buttonPanel.add(boundaryPreviewButton);
        buttonPanel.add(Box.createHorizontalStrut(15));
        buttonPanel.add(nextButton);
        buttonPanel.add(previewButtonSpacer);
        buttonPanel.add(previewButton);
@@ -1704,6 +1742,11 @@
            dikuai.setLandArea(snapshot.areaSqMeters);
            dikuai.setBaseStationCoordinates(snapshot.baseStationCoordinates);
            dikuai.setUpdateTime(getCurrentTime());
            // è®¡ç®—并设置原始边界XY坐标
            String originalBoundaryXY = convertOriginalBoundaryToXY(snapshot.originalBoundary, snapshot.baseStationCoordinates);
            if (originalBoundaryXY != null && !originalBoundaryXY.isEmpty()) {
                dikuai.setBoundaryOriginalXY(originalBoundaryXY);
            }
            Dikuai.putDikuai(landNumber, dikuai);
        }
@@ -1810,6 +1853,144 @@
        }
        return dikuai;
    }
    /**
     * å°†åŽŸå§‹è¾¹ç•Œåæ ‡ï¼ˆç»çº¬åº¦æ ¼å¼ï¼‰è½¬æ¢ä¸ºXY坐标
     * @param originalBoundary åŽŸå§‹è¾¹ç•Œåæ ‡å­—ç¬¦ä¸²ï¼Œæ ¼å¼ï¼š"lat1,lon1,alt1;lat2,lon2,alt2;..."
     * @param baseStationCoordinates åŸºå‡†ç«™åæ ‡ï¼Œæ ¼å¼ï¼š"lat,N/S,lon,E/W"
     * @return XY坐标字符串,格式:"X0,Y0;X1,Y1;X2,Y2;..." å¦‚果转换失败返回null
     */
    private static String convertOriginalBoundaryToXY(String originalBoundary, String baseStationCoordinates) {
        if (originalBoundary == null || originalBoundary.trim().isEmpty() || "-1".equals(originalBoundary.trim())) {
            return null;
        }
        if (baseStationCoordinates == null || baseStationCoordinates.trim().isEmpty()) {
            return null;
        }
        try {
            // è§£æžåŸºå‡†ç«™åæ ‡
            String[] baseParts = baseStationCoordinates.trim().split(",");
            if (baseParts.length != 4) {
                return null;
            }
            double baseLat = convertToDecimalDegree(baseParts[0], baseParts[1]);
            double baseLon = convertToDecimalDegree(baseParts[2], baseParts[3]);
            // è§£æžåŽŸå§‹è¾¹ç•Œåæ ‡
            String[] points = originalBoundary.split(";");
            StringBuilder xyStr = new StringBuilder();
            for (int i = 0; i < points.length; i++) {
                String point = points[i].trim();
                if (point.isEmpty()) {
                    continue;
                }
                String[] coords = point.split(",");
                if (coords.length >= 2) {
                    try {
                        double lat = Double.parseDouble(coords[0].trim());
                        double lon = Double.parseDouble(coords[1].trim());
                        // è½¬æ¢ä¸ºXY坐标
                        double[] xy = publicway.Gpstoxuzuobiao.convertLatLonToLocal(lat, lon, baseLat, baseLon);
                        if (xy != null && xy.length >= 2) {
                            if (xyStr.length() > 0) {
                                xyStr.append(";");
                            }
                            xyStr.append(String.format(Locale.US, "%.3f,%.3f", xy[0], xy[1]));
                        }
                    } catch (NumberFormatException e) {
                        // è·³è¿‡æ— æ•ˆçš„坐标点
                        continue;
                    }
                }
            }
            return xyStr.length() > 0 ? xyStr.toString() : null;
        } catch (Exception e) {
            System.err.println("转换原始边界坐标到XY失败: " + e.getMessage());
            return null;
        }
    }
    /**
     * é¢„览边界
     */
    private void previewBoundary() {
        if (!dikuaiData.containsKey("boundaryDrawn")) {
            JOptionPane.showMessageDialog(this, "请先完成边界绘制后再预览", "提示", JOptionPane.WARNING_MESSAGE);
            return;
        }
        // èŽ·å–æˆ–åˆ›å»ºåœ°å—å¯¹è±¡
        String landNumber = getPendingLandNumber();
        Dikuai dikuai = getOrCreatePendingDikuai();
        if (dikuai == null) {
            JOptionPane.showMessageDialog(this, "无法获取地块信息", "错误", JOptionPane.ERROR_MESSAGE);
            return;
        }
        // ç¡®ä¿åœ°å—数据是最新的
        String optimizedBoundaryXY = dikuaiData.get("optimizedBoundaryXY");
        if (optimizedBoundaryXY == null || optimizedBoundaryXY.isEmpty() || optimizedBoundaryXY.startsWith("ERROR")) {
            // å¦‚果没有优化后的边界,尝试从boundaryCoordinates获取
            String boundaryCoords = dikuaiData.get("boundaryCoordinates");
            if (boundaryCoords != null && !boundaryCoords.isEmpty() && !"-1".equals(boundaryCoords)) {
                optimizedBoundaryXY = boundaryCoords;
            } else {
                // å°è¯•从地块对象获取
                optimizedBoundaryXY = dikuai.getBoundaryCoordinates();
            }
        }
        if (optimizedBoundaryXY == null || optimizedBoundaryXY.isEmpty() || "-1".equals(optimizedBoundaryXY)) {
            JOptionPane.showMessageDialog(this, "未找到有效的边界坐标,无法预览", "提示", JOptionPane.WARNING_MESSAGE);
            return;
        }
        // ç¡®ä¿åŽŸå§‹è¾¹ç•ŒXY坐标已计算
        String originalBoundaryXY = dikuai.getBoundaryOriginalXY();
        if (originalBoundaryXY == null || originalBoundaryXY.isEmpty() || "-1".equals(originalBoundaryXY)) {
            // è®¡ç®—原始边界XY坐标
            String originalBoundary = dikuaiData.get("boundaryOriginalCoordinates");
            String baseStationCoordinates = dikuaiData.get("baseStationCoordinates");
            if (originalBoundary != null && baseStationCoordinates != null) {
                originalBoundaryXY = convertOriginalBoundaryToXY(originalBoundary, baseStationCoordinates);
                if (originalBoundaryXY != null && !originalBoundaryXY.isEmpty()) {
                    dikuai.setBoundaryOriginalXY(originalBoundaryXY);
                    Dikuai.putDikuai(landNumber, dikuai);
                }
            }
        }
        // ä¿å­˜ä¼šè¯å¿«ç…§
        captureSessionSnapshot();
        // åˆ›å»ºfinal变量供lambda使用
        final String finalOptimizedBoundaryXY = optimizedBoundaryXY;
        final Dikuai finalDikuai = dikuai;
        // å…³é—­å¯¹è¯æ¡†
        setVisible(false);
        dispose();
        // è°ƒç”¨é¦–页显示预览(与边界管理页面逻辑一致)
        SwingUtilities.invokeLater(() -> {
            Shouye.showBoundaryPreview(finalDikuai, finalOptimizedBoundaryXY, () -> {
                // è¿”回回调:重新打开新增地块对话框,并显示步骤2
                Component parent = Shouye.getInstance();
                if (parent != null) {
                    // ç¡®ä¿ä¼šè¯çŠ¶æ€æ­£ç¡®ï¼Œä»¥ä¾¿è¿”å›žæ—¶æ˜¾ç¤ºæ­¥éª¤2
                    if (activeSession != null && activeSession.drawingCompleted) {
                        resumeRequested = true;
                    }
                    showAddDikuaiDialog(parent);
                }
            });
        });
    }
    private void hideBoundaryPointSummary() {
        if (boundaryCountLabel != null) {
@@ -2010,6 +2191,8 @@
        
        if (step == 1) {
            updateObstacleSummary();
            // æ­¥éª¤1显示时,立即更新按钮状态
            SwingUtilities.invokeLater(() -> updateStep1ButtonState());
        }
        // æ›´æ–°æŒ‰é’®çŠ¶æ€
@@ -2030,6 +2213,39 @@
            if (previewButtonSpacer != null) {
                previewButtonSpacer.setVisible(false);
            }
            // æ­¥éª¤1:根据验证结果更新下一步按钮状态
            if (step == 1) {
                updateStep1ButtonState();
            }
            // æ­¥éª¤2显示边界预览按钮
            if (step == 2) {
                if (boundaryPreviewButton != null) {
                    boundaryPreviewButton.setVisible(true);
                    // æ ¹æ®æ˜¯å¦å®Œæˆè¾¹ç•Œç»˜åˆ¶æ¥è®¾ç½®æŒ‰é’®çŠ¶æ€å’ŒèƒŒæ™¯é¢œè‰²
                    boolean boundaryDrawn = dikuaiData.containsKey("boundaryDrawn");
                    boundaryPreviewButton.setEnabled(boundaryDrawn);
                    if (boundaryDrawn) {
                        boundaryPreviewButton.setBackground(PRIMARY_COLOR); // ç»¿è‰²èƒŒæ™¯
                    } else {
                        boundaryPreviewButton.setBackground(MEDIUM_GRAY); // ç°è‰²èƒŒæ™¯
                    }
                }
                // æ›´æ–°ä¸‹ä¸€æ­¥æŒ‰é’®çŠ¶æ€ï¼ˆæ ¹æ®æ˜¯å¦å®Œæˆè¾¹ç•Œç»˜åˆ¶ï¼‰
                boolean boundaryDrawn = dikuaiData.containsKey("boundaryDrawn");
                nextButton.setEnabled(boundaryDrawn);
                if (boundaryDrawn) {
                    nextButton.setBackground(PRIMARY_COLOR); // ç»¿è‰²èƒŒæ™¯
                } else {
                    nextButton.setBackground(MEDIUM_GRAY); // ç°è‰²èƒŒæ™¯
                }
                // æ›´æ–°å¼€å§‹ç»˜åˆ¶æŒ‰é’®çŠ¶æ€
                updateStartDrawingButtonState();
            } else {
                if (boundaryPreviewButton != null) {
                    boundaryPreviewButton.setVisible(false);
                    boundaryPreviewButton.setEnabled(false);
                }
            }
        } else {
            nextButton.setVisible(false);
            createButton.setVisible(true);
@@ -2039,6 +2255,10 @@
            if (previewButtonSpacer != null) {
                previewButtonSpacer.setVisible(true);
            }
            if (boundaryPreviewButton != null) {
                boundaryPreviewButton.setVisible(false);
                boundaryPreviewButton.setEnabled(false);
            }
            setPathAvailability(hasGeneratedPath());
        }
@@ -2049,6 +2269,85 @@
        }
    }
    
    /**
     * æ›´æ–°æ­¥éª¤1的下一步按钮状态
     * æ ¹æ®åœ°å—名称是否填写来设置按钮的启用状态和背景颜色
     */
    private void updateStep1ButtonState() {
        if (nextButton == null || currentStep != 1) {
            return;
        }
        String name = areaNameField.getText().trim();
        boolean canProceed = !name.isEmpty();
        nextButton.setEnabled(canProceed);
        if (canProceed) {
            // å¯ç‚¹å‡»æ—¶ï¼šç»¿è‰²èƒŒæ™¯
            nextButton.setBackground(PRIMARY_COLOR);
        } else {
            // ä¸å¯ç‚¹å‡»æ—¶ï¼šç°è‰²èƒŒæ™¯
            nextButton.setBackground(MEDIUM_GRAY);
        }
    }
    /**
     * æ›´æ–°æ­¥éª¤2的开始绘制按钮状态
     * æ ¹æ®æ˜¯å¦é€‰æ‹©äº†ç»˜åˆ¶æ–¹å¼æ¥è®¾ç½®æŒ‰é’®çš„启用状态和背景颜色
     */
    private void updateStartDrawingButtonState() {
        if (startEndDrawingBtn == null || currentStep != 2) {
            return;
        }
        boolean hasSelectedMethod = dikuaiData.containsKey("drawingMethod");
        boolean isDrawingActive = isDrawing;
        // å¦‚果正在绘制,按钮状态由toggleDrawing方法控制
        if (isDrawingActive) {
            return;
        }
        // å¦‚果已经完成绘制,按钮显示"已完成"且不可用
        boolean boundaryDrawn = dikuaiData.containsKey("boundaryDrawn");
        if (boundaryDrawn) {
            startEndDrawingBtn.setEnabled(false);
            startEndDrawingBtn.setBackground(MEDIUM_GRAY);
            return;
        }
        startEndDrawingBtn.setEnabled(hasSelectedMethod);
        if (hasSelectedMethod) {
            // å·²é€‰æ‹©ç»˜åˆ¶æ–¹å¼ï¼šç»¿è‰²èƒŒæ™¯ï¼Œå¯ç‚¹å‡»
            startEndDrawingBtn.setBackground(PRIMARY_COLOR);
        } else {
            // æœªé€‰æ‹©ç»˜åˆ¶æ–¹å¼ï¼šç°è‰²èƒŒæ™¯ï¼Œä¸å¯ç‚¹å‡»
            startEndDrawingBtn.setBackground(MEDIUM_GRAY);
        }
    }
    /**
     * æ›´æ–°æ­¥éª¤2的预览和下一步按钮状态(在完成边界绘制后调用)
     * å°†æŒ‰é’®èƒŒæ™¯é¢œè‰²è®¾ç½®ä¸ºç»¿è‰²ï¼Œè¡¨ç¤ºå¯ä»¥ç‚¹å‡»æ“ä½œ
     */
    private void updateStep2ButtonsAfterDrawing() {
        if (currentStep != 2) {
            return;
        }
        // æ›´æ–°é¢„览按钮
        if (boundaryPreviewButton != null) {
            boundaryPreviewButton.setEnabled(true);
            boundaryPreviewButton.setBackground(PRIMARY_COLOR); // ç»¿è‰²èƒŒæ™¯
        }
        // æ›´æ–°ä¸‹ä¸€æ­¥æŒ‰é’®
        if (nextButton != null) {
            nextButton.setEnabled(true);
            nextButton.setBackground(PRIMARY_COLOR); // ç»¿è‰²èƒŒæ™¯
        }
    }
    private boolean validateCurrentStep() {
        switch (currentStep) {
            case 1:
@@ -2340,6 +2639,8 @@
            showStep(2);
            showBoundaryPointSummary();
            updateBoundaryXYDisplay();
            // æ›´æ–°é¢„览和下一步按钮状态(背景颜色变绿色,可点击)
            updateStep2ButtonsAfterDrawing();
        } else {
            if (startEndDrawingBtn != null) {
                startEndDrawingBtn.setText("开始绘制");
@@ -2511,10 +2812,70 @@
        if (dikuaiData.containsKey("mowingPattern")) {
            dikuai.setMowingPattern(dikuaiData.get("mowingPattern"));
        }
        if (dikuaiData.containsKey("mowingWidth")) {
        // ä¿å­˜å‰²è‰å®½åº¦ï¼ˆä»Žæ–‡æœ¬æ¡†èŽ·å–ï¼Œå•ä½ï¼šç±³ï¼Œè½¬æ¢ä¸ºåŽ˜ç±³ä¿å­˜ï¼‰
        if (mowingWidthField != null) {
            String mowingWidthStr = mowingWidthField.getText().trim();
            if (mowingWidthStr != null && !mowingWidthStr.isEmpty()) {
                try {
                    double mowingWidthMeters = Double.parseDouble(mowingWidthStr);
                    // è½¬æ¢ä¸ºåŽ˜ç±³ä¿å­˜
                    double mowingWidthCm = mowingWidthMeters * 100.0;
                    dikuai.setMowingWidth(String.format(Locale.US, "%.2f", mowingWidthCm));
                } catch (NumberFormatException e) {
                    // å¦‚果解析失败,尝试使用dikuaiData中的值
                    if (dikuaiData.containsKey("mowingWidth")) {
                        dikuai.setMowingWidth(dikuaiData.get("mowingWidth"));
                    }
                }
            } else if (dikuaiData.containsKey("mowingWidth")) {
                dikuai.setMowingWidth(dikuaiData.get("mowingWidth"));
            }
        } else if (dikuaiData.containsKey("mowingWidth")) {
            dikuai.setMowingWidth(dikuaiData.get("mowingWidth"));
        }
        // ä¿å­˜å‰²è‰æœºå‰²åˆ€å®½åº¦ï¼ˆä»Žæ–‡æœ¬æ¡†èŽ·å–ï¼Œå•ä½ï¼šç±³ï¼‰
        if (bladeWidthField != null) {
            String bladeWidthStr = bladeWidthField.getText().trim();
            if (bladeWidthStr != null && !bladeWidthStr.isEmpty()) {
                try {
                    double bladeWidthMeters = Double.parseDouble(bladeWidthStr);
                    // ä¿å­˜ä¸ºç±³ï¼Œä¿ç•™2位小数
                    dikuai.setMowingBladeWidth(String.format(Locale.US, "%.2f", bladeWidthMeters));
                } catch (NumberFormatException e) {
                    // è§£æžå¤±è´¥æ—¶ï¼Œä¿å­˜åŽŸå§‹å­—ç¬¦ä¸²
                    dikuai.setMowingBladeWidth(bladeWidthStr);
                }
            }
        }
        // ä¿å­˜å‰²è‰å®‰å…¨è·ç¦»ï¼ˆä»Žæ–‡æœ¬æ¡†èŽ·å–ï¼Œå•ä½ï¼šç±³ï¼‰
        if (safetyDistanceField != null) {
            String safetyDistanceStr = safetyDistanceField.getText().trim();
            if (safetyDistanceStr != null && !safetyDistanceStr.isEmpty()) {
                try {
                    double safetyDistanceMeters = Double.parseDouble(safetyDistanceStr);
                    // ä¿å­˜ä¸ºç±³ï¼Œä¿ç•™2位小数
                    String formattedValue = String.format(Locale.US, "%.2f", safetyDistanceMeters);
                    dikuai.setMowingSafetyDistance(formattedValue);
                    // åŒæ—¶ä¿å­˜åˆ°dikuaiData中,以便后续使用
                    dikuaiData.put("mowingSafetyDistance", formattedValue);
                } catch (NumberFormatException e) {
                    // è§£æžå¤±è´¥æ—¶ï¼Œä¿å­˜åŽŸå§‹å­—ç¬¦ä¸²
                    dikuai.setMowingSafetyDistance(safetyDistanceStr);
                    dikuaiData.put("mowingSafetyDistance", safetyDistanceStr);
                }
            } else if (dikuaiData.containsKey("mowingSafetyDistance")) {
                // å¦‚果文本框为空,尝试从dikuaiData获取
                dikuai.setMowingSafetyDistance(dikuaiData.get("mowingSafetyDistance"));
            }
        } else if (dikuaiData.containsKey("mowingSafetyDistance")) {
            // å¦‚æžœsafetyDistanceField为null,从dikuaiData获取
            dikuai.setMowingSafetyDistance(dikuaiData.get("mowingSafetyDistance"));
        }
        // ä¿å­˜å‰²è‰è·¯å¾„坐标
        String plannedPath = dikuaiData.get("plannedPath");
        if (isMeaningfulValue(plannedPath)) {
            dikuai.setPlannedPath(plannedPath);
@@ -2569,6 +2930,13 @@
        if (resumeRequested && activeSession != null) {
            dialog.applySessionData(activeSession);
            resumeRequested = false;
            // å¦‚果会话已生成路径,优先显示步骤3
            if (activeSession.data != null && isMeaningfulValue(activeSession.data.get("plannedPath"))) {
                dialog.showStep(3);
            } else if (activeSession.drawingCompleted) {
                // å¦‚果会话已完成绘制,显示步骤2
                dialog.showStep(2);
            }
        }
        
        dialog.setVisible(true);