张世豪
6 小时以前 8d3989dff1164588d45dbb30506da1a6e1094005
解决了车头朝向的问题
已修改8个文件
已添加2个文件
801 ■■■■ 文件已修改
.classpath 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
set.properties 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/dikuai/daohangyulan.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/gecaoji/Device.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/gecaoji/GecaojiMeg.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/gecaoji/Getgecaojiimu_data.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/sendMQTT/ControlCommandSender.java 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/sendMQTT/HTTPUtils/ControlCommand.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/sendMQTT/Server.java 112 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/zhuye/Shouye.java 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.classpath
@@ -1,20 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
    <classpathentry kind="src" path="src"/>
    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
        <attributes>
            <attribute name="module" value="true"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="lib" path="D:/eclipseworkspace/GIT/GeCaoAPP/lib/jackson-annotations-2.15.2.jar"/>
    <classpathentry kind="lib" path="D:/eclipseworkspace/GIT/GeCaoAPP/lib/jackson-core-2.15.2.jar"/>
    <classpathentry kind="lib" path="D:/eclipseworkspace/GIT/GeCaoAPP/lib/jackson-databind-2.15.2.jar"/>
    <classpathentry kind="lib" path="D:/eclipseworkspace/GIT/GeCaoAPP/lib/jSerialComm-2.10.4.jar"/>
    <classpathentry kind="lib" path="D:/eclipseworkspace/GIT/GeCaoAPP/lib/jts-core-1.19.0.jar"/>
    <classpathentry kind="lib" path="D:/eclipseworkspace/GIT/GeCaoAPP/lib/lombok-1.18.36.jar"/>
    <classpathentry kind="lib" path="D:/eclipseworkspace/GIT/GeCaoAPP/lib/MQTT-1.0-SNAPSHOT.jar"/>
    <classpathentry kind="lib" path="D:/eclipseworkspace/GIT/GeCaoAPP/lib/org.eclipse.paho.client.mqttv3-1.2.2.jar"/>
    <classpathentry kind="lib" path="D:/eclipseworkspace/GIT/GeCaoAPP/lib/slf4j-api-1.7.30.jar"/>
    <classpathentry kind="lib" path="D:/eclipseworkspace/GIT/GeCaoAPP/lib/slf4j-simple-1.7.30.jar"/>
    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/jackson-annotations-2.15.2.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/jackson-core-2.15.2.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/jackson-databind-2.15.2.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/jSerialComm-2.10.4.jar"/>
    <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/lombok-1.18.36.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/MQTT-1.0-SNAPSHOT.jar"/>
    <classpathentry kind="lib" path="E:/Users/hxzk/eclipse-workspace/GeCaoAPP/lib/org.eclipse.paho.client.mqttv3-1.2.2.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="output" path="bin"/>
</classpath>
set.properties
@@ -1,19 +1,19 @@
#Mower Configuration Properties - Updated
#Sun Dec 28 20:35:53 GMT+08:00 2025
#Current work land selection updated
#Mon Dec 29 14:26:39 CST 2025
appVersion=-1
simCardNumber=-1
currentWorkLandNumber=LAND1
serialBaudRate=115200
boundaryLengthVisible=false
idleTrailDurationSeconds=60
handheldMarkerId=1872
viewCenterX=-60.00
viewCenterY=-34.94
manualBoundaryDrawingMode=false
mowerId=6258
serialPortName=COM15
serialAutoConnect=true
mapScale=8.60
measurementModeEnabled=false
firmwareVersion=-1
currentWorkLandNumber=LAND1
cuttingWidth=200
firmwareVersion=-1
handheldMarkerId=1872
idleTrailDurationSeconds=60
manualBoundaryDrawingMode=false
mapScale=12.32
measurementModeEnabled=false
mowerId=6258
serialAutoConnect=true
serialBaudRate=115200
serialPortName=COM15
simCardNumber=-1
viewCenterX=-53.78
viewCenterY=-35.02
src/dikuai/daohangyulan.java
@@ -471,12 +471,13 @@
        }
        
        // å›¾æ ‡é»˜è®¤æœä¸‹ï¼ˆ270度),需要计算旋转角度使车头朝向行驶方向
        // å¦‚果运动方向向上(90度)→ éœ€è¦æ—‹è½¬180度(270 - 90 = 180)
        // å¦‚果运动方向向右(0度)→ éœ€è¦æ—‹è½¬270度(270 - 0 = 270)
        // å¦‚果运动方向向左(180度)→ éœ€è¦æ—‹è½¬90度(270 - 180 = 90)
        // å¦‚果运动方向向下(270度)→ éœ€è¦æ—‹è½¬0度(270 - 270 = 0)
        // å…¬å¼ï¼šheading = (270 - atan2Angle + 360) % 360
        double heading = (270.0 - atan2Angle + 360.0) % 360.0;
        // åæ ‡ç³»ï¼šNorth(+Y) -> Screen Down(90). East(+X) -> Screen Right(0).
        // atan2: North(90), East(0).
        // ç›®æ ‡ï¼šNorth -> Icon Down(0 rot). East -> Icon Right(270 rot).
        // 90 -> 0.
        // 0 -> 270.
        // å…¬å¼ï¼šheading = (atan2Angle + 270) % 360
        double heading = (atan2Angle + 270.0) % 360.0;
        
        return heading;
    }
src/gecaoji/Device.java
@@ -66,8 +66,6 @@
    // å®žæ—¶é€Ÿåº¦
    private String heading;
    // èˆªå‘è§’
    private String pitch;
    // ä¿¯ä»°è§’
    private String roll; // æ¨ªæ»šè§’ è§’度
    private String yaw; // åèˆªè§’ è§’度
    private String battery_level; // ç”µæ± ç”µé‡ç™¾åˆ†æ¯”
@@ -166,7 +164,6 @@
        if (realtimeY != null) properties.setProperty("realtimeY", realtimeY);
        if (realtimeSpeed != null) properties.setProperty("realtimeSpeed", realtimeSpeed);
        if (heading != null) properties.setProperty("heading", heading);
        if (pitch != null) properties.setProperty("pitch", pitch);
        if (roll != null) properties.setProperty("roll", roll);
        if (yaw != null) properties.setProperty("yaw", yaw);
        if (battery_level != null) properties.setProperty("battery_level", battery_level);
@@ -225,7 +222,6 @@
        target.realtimeY = properties.getProperty("realtimeY", "-1");
        target.realtimeSpeed = properties.getProperty("realtimeSpeed", "-1");
        target.heading = properties.getProperty("heading", "-1");
        target.pitch = properties.getProperty("pitch", "-1");
        target.roll = properties.getProperty("roll", "-1");
        target.yaw = properties.getProperty("yaw", "-1");
        target.battery_level = properties.getProperty("battery_level", "-1");
@@ -276,7 +272,6 @@
        target.realtimeY = "-1";
        target.realtimeSpeed = "-1";
        target.heading = "-1";
        target.pitch = "-1";
        target.roll = "-1";
        target.yaw = "-1";
        target.battery_level = "-1";
@@ -384,9 +379,6 @@
            case "heading":
                this.heading = value;
                return true;
            case "pitch":
                this.pitch = value;
                return true;
            case "roll":
                this.roll = value;
                return true;
@@ -547,7 +539,7 @@
        satelliteCount = defaultIfEmpty(sanitizeField(fields, 7));
        differentialAge = defaultIfEmpty(sanitizeField(fields, 13));
        battery_level = defaultIfEmpty(sanitizeField(fields, 16));
        pitch = defaultIfEmpty(sanitizeField(fields, 17));
        yaw = defaultIfEmpty(sanitizeField(fields, 17));
        realtimeSpeed = defaultIfEmpty(sanitizeField(fields, 18));
        GupdateTime = String.valueOf(System.currentTimeMillis());
    }
@@ -924,14 +916,6 @@
        this.heading = heading;
    }
    public String getPitch() { // èŽ·å–ä¿¯ä»°è§’
        return pitch;
    }
    public void setPitch(String pitch) { // è®¾ç½®ä¿¯ä»°è§’
        this.pitch = pitch;
    }
    public String getRoll() {
        return roll;
    }
@@ -1138,7 +1122,6 @@
                ", realtimeY='" + realtimeY + '\'' +
                ", realtimeSpeed='" + realtimeSpeed + '\'' +
                ", heading='" + heading + '\'' +
                ", pitch='" + pitch + '\'' +
                ", roll='" + roll + '\'' +
                ", yaw='" + yaw + '\'' +
                ", battery_level='" + battery_level + '\'' +
src/gecaoji/GecaojiMeg.java
@@ -40,7 +40,6 @@
    private JLabel errorCodeLabel;
    private JLabel errorMessageLabel;
    private JLabel rollLabel;
    private JLabel pitchLabel;
    private JLabel yawLabel;
    
    private JLabel updateTimeLabel;
@@ -249,9 +248,6 @@
        // æ¨ªæ»šè§’
        addLabelRow(grid, gbc, currentRow++, "横滚角:", rollLabel = createValueLabel(), titleFont, valueFont);
        
        // ä¿¯ä»°è§’
        addLabelRow(grid, gbc, currentRow++, "俯仰角:", pitchLabel = createValueLabel(), titleFont, valueFont);
        // åèˆªè§’
        addLabelRow(grid, gbc, currentRow++, "偏航角:", yawLabel = createValueLabel(), titleFont, valueFont);
@@ -380,7 +376,6 @@
        errorCodeLabel = null;
        errorMessageLabel = null;
        rollLabel = null;
        pitchLabel = null;
        yawLabel = null;
        
        updateTimeLabel = null;
@@ -419,7 +414,6 @@
        errorCodeLabel.setText(formatDeviceValue(device.getError_code()));
        errorMessageLabel.setText(formatDeviceValue(device.getError_message()));
        rollLabel.setText(formatDeviceValue(device.getRoll()));
        pitchLabel.setText(formatDeviceValue(device.getPitch()));
        yawLabel.setText(formatDeviceValue(device.getYaw()));
        
        updateTimeLabel.setText(formatTimestamp(device.getGupdateTime()));
@@ -445,7 +439,6 @@
        if (errorCodeLabel != null) errorCodeLabel.setText(value);
        if (errorMessageLabel != null) errorMessageLabel.setText(value);
        if (rollLabel != null) rollLabel.setText(value);
        if (pitchLabel != null) pitchLabel.setText(value);
        if (yawLabel != null) yawLabel.setText(value);
        
        if (updateTimeLabel != null) updateTimeLabel.setText(value);
src/gecaoji/Getgecaojiimu_data.java
@@ -9,9 +9,6 @@
                return;
            }
            
            if (status.getPitch() != null) {
                device.setPitch(String.valueOf(status.getPitch()));
            }
            if (status.getRoll() != null) {
                device.setRoll(String.valueOf(status.getRoll()));
            }
src/sendMQTT/ControlCommandSender.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,266 @@
package sendMQTT;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import sendMQTT.HTTPUtils.ControlCommand;
import set.Setsys;
import user.Usrdell;
import java.io.UnsupportedEncodingException;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
/**
 * è®¾å¤‡æŽ§åˆ¶æŒ‡ä»¤MQTT发送工具类
 * ç”¨äºŽå‘设备发送控制指令(启动、停止、暂停、恢复、紧急停止等)
 */
public class ControlCommandSender {
    private static final String DEFAULT_HOST = "tcp://39.99.43.227:1883";
    private static final String DEFAULT_CLIENT_ID_PREFIX = "control_";
    private final String host;
    private final String clientId;
    /**
     * æž„造函数
     * @param host MQTT broker地址,例如 "tcp://39.99.43.227:1883"
     * @param clientId MQTT客户端ID
     */
    public ControlCommandSender(String host, String clientId) {
        this.host = host != null ? host : DEFAULT_HOST;
        this.clientId = clientId != null ? clientId : (DEFAULT_CLIENT_ID_PREFIX + System.currentTimeMillis());
    }
    /**
     * ä½¿ç”¨é»˜è®¤é…ç½®åˆ›å»ºå‘送器
     * @return ControlCommandSender实例
     */
    public static ControlCommandSender createDefault() {
        return new ControlCommandSender(DEFAULT_HOST, DEFAULT_CLIENT_ID_PREFIX + System.currentTimeMillis());
    }
    /**
     * å‘送控制指令
     *
     * @param msgId æ¶ˆæ¯å”¯ä¸€æ ‡è¯†
     * @param userId ç”¨æˆ·ID
     * @param deviceId è®¾å¤‡ç¼–号
     * @param command æŽ§åˆ¶å‘½ä»¤ï¼šstart(启动), stop(停止), pause(暂停), resume(恢复), emergency_stop(紧急停止)
     * @param value1 å‚æ•°1(可选,Double类型)
     * @param value2 å‚æ•°2(可选,String类型)
     * @param value3 å‚æ•°3(可选,Integer类型)
     * @return æ˜¯å¦å‘送成功
     * @throws MqttException MQTT发送异常
     */
    public boolean sendControlCommand(String msgId, String userId, String deviceId,
                                     String command, Double value1, String value2, Integer value3)
            throws MqttException {
        // æž„建参数对象
        ControlCommand.ControlParameters parameters = new ControlCommand.ControlParameters(
                value1, value2, value3
        );
        // æž„建控制指令对象
        long timestamp = System.currentTimeMillis();
        ControlCommand controlCommand = new ControlCommand(
                msgId,
                timestamp,
                userId,
                deviceId,
                command,
                parameters
        );
        // è½¬æ¢ä¸ºJSON字符串
        String jsonString = toJsonString(controlCommand);
        if (jsonString == null) {
            throw new MqttException(new Exception("JSON序列化失败"));
        }
        // å‘送MQTT消息
        return sendMQTT(jsonString, userId, deviceId);
    }
    /**
     * å°†ControlCommand对象转换为JSON字符串
     */
    private String toJsonString(ControlCommand command) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(command);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * å‘送MQTT消息
     *
     * @param messageJson JSON消息内容
     * @param userId ç”¨æˆ·ID
     * @param deviceId è®¾å¤‡ç¼–号
     * @return æ˜¯å¦å‘送成功
     * @throws MqttException MQTT发送异常
     */
    private boolean sendMQTT(String messageJson, String userId, String deviceId) throws MqttException {
        MqttClient client = null;
        try {
            // æž„建主题:app/{user_id}/mower/{device_id}/control
            String topic = String.format("app/%s/mower/%s/control", userId, deviceId);
            // åˆ›å»ºMQTT连接选项
            MqttConnectOptions options = new MqttConnectOptions();
            options.setCleanSession(true);
            // åˆ›å»ºMQTT客户端并连接
            client = new MqttClient(host, clientId);
            client.connect(options);
            // åˆ›å»ºæ¶ˆæ¯å¹¶å‘送
            MqttMessage message = new MqttMessage();
            message.setPayload(messageJson.getBytes("UTF-8"));
            client.publish(topic, message);
            return true;
        } catch (UnsupportedEncodingException e) {
            throw new MqttException(e);
        } finally {
            // æ–­å¼€è¿žæŽ¥
            if (client != null && client.isConnected()) {
                try {
                    client.disconnect();
                    client.close();
                } catch (MqttException e) {
                    // å¿½ç•¥æ–­å¼€è¿žæŽ¥æ—¶çš„异常
                }
            }
        }
    }
    static String msgId = "hxzkcontrol_hxzkcontrol_20151104" ;
    String userId=Usrdell.getUserEmail();//用户ID
    String deviceId=Setsys.getMowerIdValue();//设备ID
    private static double battery = 90.0;
    private static String token = "abcd123ds";
    private static int type = 0;
    /**
     * å¯åŠ¨å‘½ä»¤ï¼ˆstart)
     * â—åŠŸèƒ½ï¼šå¼€å§‹æ‰§è¡Œé¢„è®¾çš„è·¯å¾„è§„åˆ’ä»»åŠ¡æˆ–ä»Žæš‚åœçŠ¶æ€ç»§ç»­è¿è¡Œ
     * â—åº”用场景:开始割草作业、继续被暂停的任务
     *
     * @param userId ç”¨æˆ·ID
     * @param deviceId è®¾å¤‡ID
     * @return å‘送结果,true表示成功,false表示失败
     */
    public static boolean sendStartCommand(String userId, String deviceId) {
        try {
            ControlCommandSender sender = ControlCommandSender.createDefault();
            boolean result = sender.sendControlCommand(msgId, userId, deviceId, "start", battery, token, type);
            return result;
        } catch (Exception e) {
            System.err.println("启动命令发送失败: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    /**
     * åœæ­¢å‘½ä»¤ï¼ˆstop)
     * â—åŠŸèƒ½ï¼šæ­£å¸¸åœæ­¢å‰²è‰æœºè¿è¡Œï¼Œä¿å­˜å½“å‰çŠ¶æ€
     * â—åº”用场景:完成作业后的正常停止、用户主动停止
     * â—ç‰¹ç‚¹ï¼šå¹³ç¨³å‡é€Ÿåœæ­¢ï¼Œè®°å½•停止位置
     *
     * @param userId ç”¨æˆ·ID
     * @param deviceId è®¾å¤‡ID
     * @return å‘送结果,true表示成功,false表示失败
     */
    public static boolean sendStopCommand(String userId, String deviceId) {
        try {
            ControlCommandSender sender = ControlCommandSender.createDefault();
            boolean result = sender.sendControlCommand(msgId, userId, deviceId, "stop", battery, token, type);
            return result;
        } catch (Exception e) {
            System.err.println("停止命令发送失败: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    /**
     * æš‚停命令(pause)
     * â—åŠŸèƒ½ï¼šä¸´æ—¶æš‚åœå½“å‰è¿è¡Œï¼Œä¿æŒè®¾å¤‡å°±ç»ªçŠ¶æ€
     * â—åº”用场景:临时检查、避让障碍物、用户临时中断
     * â—ç‰¹ç‚¹ï¼šå¯å¿«é€Ÿæ¢å¤ï¼Œæ— éœ€é‡æ–°åˆå§‹åŒ–
     *
     * @param userId ç”¨æˆ·ID
     * @param deviceId è®¾å¤‡ID
     * @return å‘送结果,true表示成功,false表示失败
     */
    public static boolean sendPauseCommand(String userId, String deviceId) {
        try {
            ControlCommandSender sender = ControlCommandSender.createDefault();
            boolean result = sender.sendControlCommand(msgId, userId, deviceId, "pause", battery, token, type);
            return result;
        } catch (Exception e) {
            System.err.println("暂停命令发送失败: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    /**
     * æ¢å¤å‘½ä»¤ï¼ˆresume)
     * â—åŠŸèƒ½ï¼šä»Žæš‚åœçŠ¶æ€æ¢å¤ä¹‹å‰çš„è¿è¡Œ
     * â—åº”用场景:暂停后继续作业
     * â—ç‰¹ç‚¹ï¼šä»Žæš‚停点继续执行,保持路径连续性
     *
     * @param userId ç”¨æˆ·ID
     * @param deviceId è®¾å¤‡ID
     * @return å‘送结果,true表示成功,false表示失败
     */
    public static boolean sendResumeCommand(String userId, String deviceId) {
        try {
            ControlCommandSender sender = ControlCommandSender.createDefault();
            boolean result = sender.sendControlCommand(msgId, userId, deviceId, "resume", battery, token, type);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * ç´§æ€¥åœæ­¢å‘½ä»¤ï¼ˆemergency_stop)
     * â—åŠŸèƒ½ï¼šç«‹å³åœæ­¢æ‰€æœ‰åŠ¨ä½œï¼Œåˆ‡æ–­åŠ¨åŠ›è¾“å‡º
     * â—åº”用场景:紧急情况、安全威胁、设备异常
     * â—ç‰¹ç‚¹ï¼šæœ€é«˜ä¼˜å…ˆçº§ï¼Œéœ€è¦æ‰‹åŠ¨å¤ä½æ‰èƒ½é‡æ–°å¯åŠ¨
     *
     * @param userId ç”¨æˆ·ID
     * @param deviceId è®¾å¤‡ID
     * @return å‘送结果,true表示成功,false表示失败
     */
    public static boolean sendEmergencyStopCommand(String userId, String deviceId) {
        try {
            ControlCommandSender sender = ControlCommandSender.createDefault();
            boolean result = sender.sendControlCommand(msgId, userId, deviceId, "emergency_stop", battery, token, type);
                        return result;
        } catch (Exception e) {
            System.err.println("紧急停止命令发送失败: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
}
src/sendMQTT/HTTPUtils/ControlCommand.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,134 @@
package sendMQTT.HTTPUtils;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
 * è®¾å¤‡æŽ§åˆ¶æŒ‡ä»¤JSON结构
 * ä¸»é¢˜ï¼šapp/{user_id}/mower/{device_id}/control
 */
public class ControlCommand {
    @JsonProperty("msg_id")
    private String msgId;
    private long timestamp;
    @JsonProperty("user_id")
    private String userId;
    @JsonProperty("device_id")
    private String deviceId;
    private String command; // start, stop, pause, resume, emergency_stop
    private ControlParameters parameters;
    public ControlCommand() {
    }
    public ControlCommand(String msgId, long timestamp, String userId, String deviceId,
                         String command, ControlParameters parameters) {
        this.msgId = msgId;
        this.timestamp = timestamp;
        this.userId = userId;
        this.deviceId = deviceId;
        this.command = command;
        this.parameters = parameters;
    }
    public String getMsgId() {
        return msgId;
    }
    public void setMsgId(String msgId) {
        this.msgId = msgId;
    }
    public long getTimestamp() {
        return timestamp;
    }
    public void setTimestamp(long timestamp) {
        this.timestamp = timestamp;
    }
    public String getUserId() {
        return userId;
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
    public String getDeviceId() {
        return deviceId;
    }
    public void setDeviceId(String deviceId) {
        this.deviceId = deviceId;
    }
    public String getCommand() {
        return command;
    }
    public void setCommand(String command) {
        this.command = command;
    }
    public ControlParameters getParameters() {
        return parameters;
    }
    public void setParameters(ControlParameters parameters) {
        this.parameters = parameters;
    }
    /**
     * æŽ§åˆ¶å‚数内部类
     */
    public static class ControlParameters {
        private Double value1;
        private String value2;
        private Integer value3;
        public ControlParameters() {
        }
        public ControlParameters(Double value1, String value2, Integer value3) {
            this.value1 = value1;
            this.value2 = value2;
            this.value3 = value3;
        }
        public Double getValue1() {
            return value1;
        }
        public void setValue1(Double value1) {
            this.value1 = value1;
        }
        public String getValue2() {
            return value2;
        }
        public void setValue2(String value2) {
            this.value2 = value2;
        }
        public Integer getValue3() {
            return value3;
        }
        public void setValue3(Integer value3) {
            this.value3 = value3;
        }
    }
}
src/sendMQTT/Server.java
@@ -16,66 +16,66 @@
public class Server {
   static String host="tcp://39.99.43.227:1883";
   static String clientId="1231@qq.com";
    static String host="tcp://39.99.43.227:1883";
    static String clientId="1231@qq.com";
    /**
     * èŽ·å–å½“å‰ç³»ç»Ÿæ—¶é—´
     * @return æ ¼å¼åŒ–的时间字符串,格式为 "yyyy-MM-dd HH:mm:ss"
     */
    public static String getCurrentSystemTime() {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return now.format(formatter);
    }
    /**
     * èŽ·å–å½“å‰ç³»ç»Ÿæ—¶é—´
     * @return æ ¼å¼åŒ–的时间字符串,格式为 "yyyy-MM-dd HH:mm:ss"
     */
    public static String getCurrentSystemTime() {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return now.format(formatter);
    }
    /**
     * èŽ·å–å½“å‰ç³»ç»Ÿæ—¶é—´ï¼ˆè‡ªå®šä¹‰æ ¼å¼ï¼‰
     * @param pattern æ—¶é—´æ ¼å¼æ¨¡å¼ï¼Œä¾‹å¦‚ "yyyy-MM-dd HH:mm:ss"、"yyyy/MM/dd HH:mm:ss" ç­‰
     * @return æ ¼å¼åŒ–的时间字符串
     */
    public static String getCurrentSystemTime(String pattern) {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return now.format(formatter);
    }
    /**
     * èŽ·å–å½“å‰ç³»ç»Ÿæ—¶é—´ï¼ˆè‡ªå®šä¹‰æ ¼å¼ï¼‰
     * @param pattern æ—¶é—´æ ¼å¼æ¨¡å¼ï¼Œä¾‹å¦‚ "yyyy-MM-dd HH:mm:ss"、"yyyy/MM/dd HH:mm:ss" ç­‰
     * @return æ ¼å¼åŒ–的时间字符串
     */
    public static String getCurrentSystemTime(String pattern) {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return now.format(formatter);
    }
    private static void testCode() {
        String mes_id="hxzkcontrol_20151104";//消息ID
        String userId=Usrdell.getUserEmail();//用户ID
        String deviceID=Setsys.getMowerIdValue();//设备ID
        String protiesFilePath="./dikuai.properties";//地块信息配置文件
        String json = PropertiesFileUploader.movePathSend(mes_id,userId, deviceID, protiesFilePath);//生成给设备下发的JSON
        sendMQTT(json, host, clientId,deviceID);
    }
    /**发送割草路径*/
    public static void sendMowingPath() {
        String mes_id="hxzkcontrol_20151104";//消息ID
        String userId=Usrdell.getUserEmail();//用户ID
        String deviceID=Setsys.getMowerIdValue();//设备ID
        String protiesFilePath="./dikuai.properties";//地块信息配置文件
        String json = PropertiesFileUploader.movePathSend(mes_id,userId, deviceID, protiesFilePath);//生成给设备下发的JSON
        sendMQTT(json, host, clientId,deviceID);
    }
    private static void sendMQTT(String messageJson, String host, String clientId,String deviceID)  {
        try {
            String topic="app/p/mower/"+deviceID+"/path";
            MqttConnectOptions options = new MqttConnectOptions();
            options.setCleanSession(true);
            MqttClient client = new MqttClient(host, clientId);
            client.connect(options);
            // æ·»åŠ å…³é—­é’©å­ï¼Œç¨‹åºé€€å‡ºæ—¶å…³é—­è¿žæŽ¥
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                try {
                    if (client.isConnected()) {
                        client.disconnect();
                        client.close();
                    }
                } catch (Exception e) {
                }
            }));
            MqttMessage message = new MqttMessage();
            message.setPayload(messageJson.getBytes("UTF-8"));
            client.publish(topic, message);
        } catch (MqttException e) {
            throw new RuntimeException(e);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
    private static void sendMQTT(String messageJson, String host, String clientId,String deviceID)  {
        try {
            String topic="app/p/mower/"+deviceID+"/path";
            MqttConnectOptions options = new MqttConnectOptions();
            options.setCleanSession(true);
            MqttClient client = new MqttClient(host, clientId);
            client.connect(options);
            // æ·»åŠ å…³é—­é’©å­ï¼Œç¨‹åºé€€å‡ºæ—¶å…³é—­è¿žæŽ¥
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                try {
                    if (client.isConnected()) {
                        client.disconnect();
                        client.close();
                    }
                } catch (Exception e) {
                }
            }));
            MqttMessage message = new MqttMessage();
            message.setPayload(messageJson.getBytes("UTF-8"));
            client.publish(topic, message);
        } catch (MqttException e) {
            throw new RuntimeException(e);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
}
src/zhuye/Shouye.java
@@ -9,9 +9,11 @@
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import chuankou.dellmessage;
import chuankou.sendmessage;
import chuankou.DataListener;
import chuankou.SerialPortService;
import dikuai.Dikuai;
import dikuai.Dikuaiguanli;
@@ -32,11 +34,12 @@
import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
// import java.util.function.Consumer;
import chuankou.DataListener;
import java.awt.geom.Point2D;
import publicway.Gpstoxuzuobiao;
import sendMQTT.ControlCommandSender;
import sendMQTT.Server;
import user.Usrdell;
import dikuai.Dikuaiguanli;
import publicway.Fanhuibutton;
import publicway.Gpstoxuzuobiao;
/**
 * é¦–页界面 - é€‚配6.5寸竖屏,使用独立的MapRenderer进行绘制
@@ -1782,6 +1785,17 @@
                return;
            }
            
            // æ£€æŸ¥è·¯å¾„ID是否匹配
            if (!checkPathIdMatch()) {
                startButtonShowingPause = true;
                statusLabel.setText("待机");
                updateStartButtonAppearance();
                return;
            }
            // å‘送开始指令
            sendControlCommand("start");
            statusLabel.setText("作业中");
            if (stopButtonActive) {
                stopButtonActive = false;
@@ -1794,6 +1808,8 @@
            return;
        }
        } else {
            // å‘送暂停指令
            sendControlCommand("pause");
            statusLabel.setText("暂停中");
            pauseMowingSession();
        }
@@ -1801,6 +1817,169 @@
    }
    /**
     * æ£€æŸ¥è·¯å¾„ID是否匹配
     * @return å¦‚果匹配返回true,否则返回false
     */
    private boolean checkPathIdMatch() {
        Device device = Device.getGecaoji();
        if (device == null) {
            showCustomMessageDialog("无法获取割草机信息,请检查设备连接", "提示");
            return false;
        }
        String savedPathId = device.getPath_id_saved();
        String currentLandNumber = Dikuaiguanli.getCurrentWorkLandNumber();
        if (currentLandNumber == null) {
            showCustomMessageDialog("请先选择作业地块", "提示");
            return false;
        }
        // å¦‚果路径ID不匹配(注意处理null和-1的情况)
        boolean isMatch = savedPathId != null && savedPathId.equals(currentLandNumber);
        if (!isMatch) {
            showPathMismatchDialog(currentLandNumber);
            return false;
        }
        return true;
    }
    /**
     * æ˜¾ç¤ºè·¯å¾„不匹配对话框
     */
    private void showPathMismatchDialog(String landNumber) {
        Window parentWindow = SwingUtilities.getWindowAncestor(this);
        JDialog dialog = new JDialog(parentWindow, "路径不匹配", Dialog.ModalityType.APPLICATION_MODAL);
        dialog.setLayout(new BorderLayout(20, 20));
        dialog.setResizable(false);
        JPanel contentPanel = new JPanel(new BorderLayout(0, 15));
        contentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 10, 20));
        contentPanel.setBackground(Color.WHITE);
        JLabel messageLabel = new JLabel("<html><div style='text-align: center;'>当前割草机路径与选中地块不一致<br>是否立即发送地块 " + landNumber + " çš„路径信息?</div></html>");
        messageLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
        messageLabel.setHorizontalAlignment(SwingConstants.CENTER);
        contentPanel.add(messageLabel, BorderLayout.CENTER);
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));
        buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
        buttonPanel.setOpaque(false);
        JButton sendButton = buttonset.createStyledButton("立即发送", THEME_COLOR);
        JButton cancelButton = buttonset.createStyledButton("取消", new Color(150, 150, 150));
        sendButton.addActionListener(e -> {
            dialog.dispose();
            sendMowingPathAndCheck(landNumber);
        });
        cancelButton.addActionListener(e -> dialog.dispose());
        buttonPanel.add(sendButton);
        buttonPanel.add(cancelButton);
        contentPanel.add(buttonPanel, BorderLayout.SOUTH);
        dialog.add(contentPanel, BorderLayout.CENTER);
        dialog.pack();
        dialog.setLocationRelativeTo(this);
        dialog.setVisible(true);
    }
    /**
     * å‘送割草路径并检查结果
     */
    private void sendMowingPathAndCheck(String expectedLandNumber) {
        // æ˜¾ç¤ºå‘送中提示
        JDialog progressDialog = new JDialog(SwingUtilities.getWindowAncestor(this), "发送中", Dialog.ModalityType.MODELESS);
        progressDialog.setLayout(new BorderLayout());
        JLabel label = new JLabel("正在发送路径信息...", SwingConstants.CENTER);
        label.setBorder(BorderFactory.createEmptyBorder(20, 40, 20, 40));
        progressDialog.add(label);
        progressDialog.pack();
        progressDialog.setLocationRelativeTo(this);
        progressDialog.setVisible(true);
        // å¼‚步发送
        new Thread(() -> {
            try {
                Server.sendMowingPath();
                // è½®è¯¢æ£€æŸ¥è·¯å¾„ID是否更新
                int maxRetries = 10; // æœ€å¤šç­‰å¾…10秒
                boolean success = false;
                for (int i = 0; i < maxRetries; i++) {
                    Thread.sleep(1000);
                    Device device = Device.getGecaoji();
                    if (device != null) {
                        // åˆ·æ–°è®¾å¤‡æ•°æ®
                        device.initFromProperties();
                        String currentPathId = device.getPath_id_saved();
                        if (expectedLandNumber.equals(currentPathId)) {
                            success = true;
                            break;
                        }
                    }
                }
                final boolean finalSuccess = success;
                SwingUtilities.invokeLater(() -> {
                    progressDialog.dispose();
                    if (finalSuccess) {
                        showCustomMessageDialog("路径发送成功!", "提示");
                    } else {
                        // å³ä½¿è¶…时也提示发送完成,让用户再次尝试开始
                        showCustomMessageDialog("路径发送指令已发出,请稍后重试开始作业", "提示");
                    }
                });
            } catch (Exception e) {
                SwingUtilities.invokeLater(() -> {
                    progressDialog.dispose();
                    showCustomMessageDialog("发送失败: " + e.getMessage(), "错误");
                });
            }
        }).start();
    }
    /**
     * å‘送控制指令
     * @param command start/stop/pause/resume
     */
    private void sendControlCommand(String command) {
        new Thread(() -> {
            try {
                String userId = Usrdell.getUserEmail();
                String deviceId = Setsys.getMowerIdValue();
                String msgId = "hxzkcontrol_" + System.currentTimeMillis();
                if (userId == null || deviceId == null) {
                    System.err.println("无法发送指令:用户ID或设备ID为空");
                    return;
                }
                // ç‰¹æ®Šå¤„理:如果是resume指令(即从暂停状态再次点击开始)
                String finalCommand = command;
                if ("start".equals(command) && "暂停中".equals(statusLabel.getText())) {
                    finalCommand = "resume";
                }
                ControlCommandSender sender = ControlCommandSender.createDefault();
                sender.sendControlCommand(msgId, userId, deviceId, finalCommand, 0.0, "", 0);
            } catch (Exception e) {
                e.printStackTrace();
                SwingUtilities.invokeLater(() ->
                    showCustomMessageDialog("指令发送失败: " + e.getMessage(), "错误"));
            }
        }).start();
    }
    /**
     * æ£€æŸ¥å‰²è‰æœºæ˜¯å¦åœ¨å½“前选中的作业地块边界范围内
     * @return å¦‚果割草机在边界内返回true,否则返回false并显示提示
     */
@@ -1895,6 +2074,8 @@
        stopButtonActive = !stopButtonActive;
        updateStopButtonIcon();
        if (stopButtonActive) {
            // å‘送停止指令
            sendControlCommand("stop");
            statusLabel.setText("已结束");
            startButtonShowingPause = false;
            stopMowingSession();