liusuyi
2024-10-30 05ba868736725db54db1944ed5cf7a95d14efc37
增加:手动飞行控制
26 files added
10 files modified
1 files deleted
1282 ■■■■■ changed files
ard-framework/src/main/java/com/ard/framework/config/SecurityConfig.java 2 ●●●●● patch | view | raw | blame | history
ard-work/pom.xml 5 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/controller/UavGimbalController.java 3 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/controller/UavManualFlyController.java 73 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/http/TyClient.java 2 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/model/dto/BoxSettingsDTO.java 14 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/EmeragencyBrakeDTO.java 23 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/FlyControlDTO.java 40 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/FlyPointControlCancelDTO.java 20 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/FlyPointControlDTO.java 46 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/LandCancelDTO.java 20 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/LandDTO.java 20 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/LandForcedDTO.java 20 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/ReturnHomeCancelDTO.java 21 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/ReturnHomeDTO.java 1 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/PointFlyCmd.java 30 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/EmergencyBrakeCmd.java 35 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/EmergencyBrakeReplyCmd.java 34 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/FlyControlCmd.java 45 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/FlyPointControlCancelCmd.java 32 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/FlyPointControlCmd.java 46 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/LandCancelCmd.java 33 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/LandCancelReplyCmd.java 33 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/LandCmd.java 33 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/LandForcedCmd.java 33 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/LandForcedReplyCmd.java 33 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/LandReplyCmd.java 33 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/PointControlReachedReplyCmd.java 33 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/ReturnHomeCancelCmd.java 33 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/ReturnHomeCancelReplyCmd.java 33 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/netty/handler/MessageProtocolDecoder.java 57 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/service/ITyService.java 45 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/service/impl/TyServiceImpl.java 109 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/websocket/config/WebSocketConfig.java 27 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/websocket/handler/TyMessageHandler.java 39 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/websocket/interceptor/TyHandshakeInterceptor.java 35 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/websocket/utils/WebSocketUtil.java 141 ●●●●● patch | view | raw | blame | history
ard-framework/src/main/java/com/ard/framework/config/SecurityConfig.java
@@ -111,7 +111,7 @@
            .authorizeHttpRequests((requests) -> {
                permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
                // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
                requests.antMatchers("/login", "/register", "/captchaImage", "/ws/**").permitAll()
                    // 静态资源,可匿名访问
                    .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                    .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
ard-work/pom.xml
@@ -55,6 +55,11 @@
            <artifactId>protobuf-java</artifactId>
            <version>3.20.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
    </dependencies>
    <build>
ard-work/src/main/java/com/ard/tianyu/controller/UavGimbalController.java
@@ -41,8 +41,7 @@
    private ITyService tyService;
    @ApiOperation(value = "云台转动(基于角度)", notes = "actionCode值:0-回中,1-上,2-右上,3-右,4-右下,5-下,6-左下,7-左,8-左上,254-停止")
    @ApiOperation(value = "云台转动(基于角度)", notes = "actionCode值:0-回中,1-上,2-右上,3-右,4-右下,5-下,6-左下,7-左,8-左上,254-停止 \r\n rotationAngle值:取值范围:0~255【云台实际转动角度= 转动角度/100+2.5 单位:度】")
    @ApiOperationSupport(order = 1)
    @PostMapping("/angleControl")
    public AjaxResult angleControl(@RequestBody AngleControlDTO angleControlDto) {
ard-work/src/main/java/com/ard/tianyu/controller/UavManualFlyController.java
@@ -1,9 +1,7 @@
package com.ard.tianyu.controller;
import com.ard.common.core.domain.AjaxResult;
import com.ard.tianyu.model.dto.manual.ReturnHomeDTO;
import com.ard.tianyu.model.dto.manual.SwitchUavControlDTO;
import com.ard.tianyu.model.dto.manual.TakeOffDTO;
import com.ard.tianyu.model.dto.manual.*;
import com.ard.tianyu.service.ITyService;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.Api;
@@ -33,6 +31,7 @@
        tyService.switchUavControl(switchUavControlDTO);
        return AjaxResult.success();
    }
    @ApiOperation(value = "起飞", notes = "起飞高度height取值范围:1.2~100,误差:+-0.4米")
    @ApiOperationSupport(order = 2)
    @PostMapping("/takeOff")
@@ -48,4 +47,72 @@
        tyService.returnHome(returnHomeDTO);
        return AjaxResult.success();
    }
    @ApiOperation("取消返航")
    @ApiOperationSupport(order = 4)
    @PostMapping("/returnHomeCancel")
    public AjaxResult returnHomeCancel(@RequestBody ReturnHomeCancelDTO returnHomeCancelDTO) {
        tyService.returnHomeCancel(returnHomeCancelDTO);
        return AjaxResult.success();
    }
    @ApiOperation("降落")
    @ApiOperationSupport(order = 5)
    @PostMapping("/land")
    public AjaxResult land(@RequestBody LandDTO landDTO) {
        tyService.land(landDTO);
        return AjaxResult.success();
    }
    @ApiOperation("取消降落")
    @ApiOperationSupport(order = 6)
    @PostMapping("/landCancel")
    public AjaxResult landCancel(@RequestBody LandCancelDTO landCancelDTO) {
        tyService.landCancel(landCancelDTO);
        return AjaxResult.success();
    }
    @ApiOperation("强制降落")
    @ApiOperationSupport(order = 7)
    @PostMapping("/landForced")
    public AjaxResult landForced(@RequestBody LandForcedDTO landForcedDTO) {
        tyService.landForced(landForcedDTO);
        return AjaxResult.success();
    }
    @ApiOperation(value = "方向控制", notes = "前后速度frSpeed:【前0至30 后0至-30】单位m/s " +
            "左右速度lrSpeed:【右0至30 左0至-30】单位m/s  " +
            "上下速度udSpeed:【上0至5 后0至-5 】单位m/s 偏航角yaw:-150~150 deg/s")
    @ApiOperationSupport(order = 8)
    @PostMapping("/flyControl")
    public AjaxResult flyControl(@RequestBody FlyControlDTO flyControlDTO) {
        tyService.flyControl(flyControlDTO);
        return AjaxResult.success();
    }
    @ApiOperation(value = "开始指点飞行", notes = "速度speed:0~20 单位m/s " +
            "飞行模式flyMode:0-节能模式【飞机直线飞到目标点,需飞机在空中】," +
            "1-安全模式【飞机飞行至目标点高度再平飞到目标点,飞机可以在地面或空中】 默认为节能模式")
    @ApiOperationSupport(order = 9)
    @PostMapping("/flyPointControl")
    public AjaxResult flyPointControl(@RequestBody FlyPointControlDTO flyPointControlDTO) {
        tyService.flyPointControl(flyPointControlDTO);
        return AjaxResult.success();
    }
    @ApiOperation("停止指点飞行")
    @ApiOperationSupport(order = 10)
    @PostMapping("/flyPointControlCancel")
    public AjaxResult flyPointControlCancel(@RequestBody FlyPointControlCancelDTO flyPointControlCancelDTO) {
        tyService.flyPointControlCancel(flyPointControlCancelDTO);
        return AjaxResult.success();
    }
    @ApiOperation(value = "紧急制动", notes = "动作参数actionParam: 0-解除紧急制动 1-紧急制动")
    @ApiOperationSupport(order = 11)
    @PostMapping("/emeragencyBrake")
    public AjaxResult emeragencyBrake(@RequestBody EmeragencyBrakeDTO emeragencyBrakeDTO) {
        tyService.emeragencyBrake(emeragencyBrakeDTO);
        return AjaxResult.success();
    }
}
ard-work/src/main/java/com/ard/tianyu/http/TyClient.java
@@ -54,5 +54,5 @@
     * 修改云盒设置
     */
    @Post(TyConst.POST_BOX_UPDATE)
    TyHttpCommonReceiver updateBoxSettings(@JSONBody BoxSettingsDTO boxSettingsDTO, @Header Map<String, String> headers);
    TyHttpCommonReceiver<?> updateBoxSettings(@JSONBody BoxSettingsDTO boxSettingsDTO, @Header Map<String, String> headers);
}
ard-work/src/main/java/com/ard/tianyu/model/dto/BoxSettingsDTO.java
@@ -34,41 +34,41 @@
     * 照片实时回传开关,0-关闭,1-开启
     */
    @JsonProperty("openImgRtp")
    private String openImgRtp;
    private Integer openImgRtp;
    /**
     * 视频包排序开关,0-关闭,1-开启(排序会增加视频延时)
     */
    @JsonProperty("openVideoSort")
    private String openVideoSort;
    private Integer openVideoSort;
    /**
     * 云端视频存储开关,0-关闭,1-开启
     */
    @JsonProperty("openVideoStorage")
    private String openVideoStorage;
    private Integer openVideoStorage;
    /**
     * 视频自动推流开关,0-关闭,1-开启
     */
    @JsonProperty("videoAutoPush")
    private String videoAutoPush;
    private Integer videoAutoPush;
    /**
     * 云盒固件自动升级开关,0-关闭,1-开启
     */
    @JsonProperty("autoUpdate")
    private String autoUpdate;
    private Integer autoUpdate;
    /**
     * 云盒与平台失联xx秒后自动返航
     */
    @JsonProperty("lostTime")
    private String lostTime;
    private Integer lostTime;
    /**
     * 低电量自动返航开关:0-关闭,1-智能计算,xx-指定的返航电量
     */
    @JsonProperty("minHomeBattery")
    private String minHomeBattery;
    private Integer minHomeBattery;
}
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/EmeragencyBrakeDTO.java
New file
@@ -0,0 +1,23 @@
package com.ard.tianyu.model.dto.manual;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 紧急制动实体对象
 *
 * @author liusuyi
 * @date 2024年10月26日9:02
 **/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class EmeragencyBrakeDTO {
    private String boxSn;
    //0x00-解除紧急制动 0x01-紧急制动
    private byte actionParam;
}
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/FlyControlDTO.java
New file
@@ -0,0 +1,40 @@
package com.ard.tianyu.model.dto.manual;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 方向控制实体对象
 *
 * @author liusuyi
 * @date 2024年10月26日9:02
 **/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FlyControlDTO {
    private String boxSn;
    /**
     * 前后速度
     */
    private Float frSpeed;
    /**
     * 左右速度
     */
    private Float lrSpeed;
    /**
     * 上下速度
     */
    private Float udSpeed;
    /**
     * 偏航角
     */
    private Float yaw;
}
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/FlyPointControlCancelDTO.java
New file
@@ -0,0 +1,20 @@
package com.ard.tianyu.model.dto.manual;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 停止指点飞行实体对象
 *
 * @author liusuyi
 * @date 2024年10月26日9:02
 **/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FlyPointControlCancelDTO {
    private String boxSn;
}
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/FlyPointControlDTO.java
New file
@@ -0,0 +1,46 @@
package com.ard.tianyu.model.dto.manual;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 指点飞行控制实体对象
 *
 * @author liusuyi
 * @date 2024年10月26日9:02
 **/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FlyPointControlDTO {
    private String boxSn;
    /**
     * 经度
     */
    private Double longitude;
    /**
     * 纬度
     */
    private Double latitude;
    /**
     * 高度
     */
    private Float altitude;
    /**
     * 速度
     */
    private Float speed;
    /**
     * 飞行模式
     */
    @Builder.Default
    private byte flyMode = 0;
}
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/LandCancelDTO.java
New file
@@ -0,0 +1,20 @@
package com.ard.tianyu.model.dto.manual;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 取消降落实体对象
 *
 * @author liusuyi
 * @date 2024年10月26日9:02
 **/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class LandCancelDTO {
    private String boxSn;
}
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/LandDTO.java
New file
@@ -0,0 +1,20 @@
package com.ard.tianyu.model.dto.manual;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 降落实体对象
 *
 * @author liusuyi
 * @date 2024年10月26日9:02
 **/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class LandDTO {
    private String boxSn;
}
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/LandForcedDTO.java
New file
@@ -0,0 +1,20 @@
package com.ard.tianyu.model.dto.manual;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 取消降落实体对象
 *
 * @author liusuyi
 * @date 2024年10月26日9:02
 **/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class LandForcedDTO {
    private String boxSn;
}
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/ReturnHomeCancelDTO.java
New file
@@ -0,0 +1,21 @@
package com.ard.tianyu.model.dto.manual;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.validation.annotation.Validated;
/**
 * 取消返航实体对象
 *
 * @author liusuyi
 * @date 2024年10月26日9:02
 **/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ReturnHomeCancelDTO {
    private String boxSn;
}
ard-work/src/main/java/com/ard/tianyu/model/dto/manual/ReturnHomeDTO.java
@@ -19,7 +19,6 @@
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Validated
public class ReturnHomeDTO {
    private String boxSn;
}
ard-work/src/main/java/com/ard/tianyu/netty/command/PointFlyCmd.java
File was deleted
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/EmergencyBrakeCmd.java
New file
@@ -0,0 +1,35 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.model.dto.manual.EmeragencyBrakeDTO;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
/**
 * 紧急制动指令
 *
 * @author liusuyi
 * @date 2024年10月30日16:20
 **/
public class EmergencyBrakeCmd extends BaseCmd {
    private final byte actionParam;         //动作参数
    public EmergencyBrakeCmd(EmeragencyBrakeDTO emeragencyBrakeDTO) {
        setDataHeader((short) 0x7479);
        setDataLength((short) 19);
        setBoxSn(emeragencyBrakeDTO.getBoxSn().getBytes());
        setCmdCode((byte) 0xD1);
        this.actionParam = emeragencyBrakeDTO.getActionParam();
    }
    public void encode(ByteBuf out) {
        out.writeShort(getDataHeader());    // 写入数据头
        out.writeShort(getDataLength());    // 写入数据长度
        out.writeBytes(getBoxSn());         // 写入盒子SN
        out.writeByte(getCmdCode());        // 写入命令码
        out.writeByte(0x00);          // 写入加密标志
        out.writeByte(0x32);          // 写入动作编号
        out.writeByte(this.actionParam);    // 写入动作参数
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/EmergencyBrakeReplyCmd.java
New file
@@ -0,0 +1,34 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.model.dto.manual.EmeragencyBrakeDTO;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
import lombok.Data;
/**
 * 紧急制动回复指令
 *
 * @author liusuyi
 * @date 2024年10月30日16:20
 **/
@Data
public class EmergencyBrakeReplyCmd extends BaseCmd {
    private final byte encryptFlag = 0x00; // 加密标志
    private final byte actionCode = 0x32;  // 动作编号
    private final byte result;             // 执行结果
    private final Integer errorCode;       // 错误码
    public EmergencyBrakeReplyCmd(String boxSn, Byte result, Integer errorCode) {
        setDataHeader((short) 0x6A77);
        setDataLength((short) 23);
        setBoxSn(boxSn.getBytes());
        setCmdCode((byte) 0xD1);
        this.result = result;
        this.errorCode = errorCode;
    }
    public void encode(ByteBuf out) {
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/FlyControlCmd.java
New file
@@ -0,0 +1,45 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.model.dto.manual.FlyControlDTO;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
/**
 * 方向控制指令
 *
 * @author liusuyi
 * @date 2024年10月26日15:43
 **/
public class FlyControlCmd extends BaseCmd {
    private final float frSpeed;                    // 前后速度
    private final float lrSpeed;                    // 左右速度
    private final float udSpeed;                    // 上下速度
    private final float yaw;                        // 偏航角
    private final short execTime = 200;             // 执行时间
    public FlyControlCmd(FlyControlDTO flyControlDTO) {
        setDataHeader((short) 0x7479);
        setDataLength((short) 36);
        setBoxSn(flyControlDTO.getBoxSn().getBytes());
        setCmdCode((byte) 0xD1);
        this.frSpeed = flyControlDTO.getFrSpeed();
        this.lrSpeed = flyControlDTO.getLrSpeed();
        this.udSpeed = flyControlDTO.getUdSpeed();
        this.yaw = flyControlDTO.getYaw();
    }
    public void encode(ByteBuf out) {
        out.writeShort(getDataHeader());    // 写入数据头
        out.writeShort(getDataLength());    // 写入数据长度
        out.writeBytes(getBoxSn());         // 写入盒子SN
        out.writeByte(getCmdCode());        // 写入命令码
        out.writeByte(0x00);          // 写入加密标志
        out.writeByte(0x28);          // 写入动作编号
        out.writeFloat(frSpeed);            // 写入前后速度
        out.writeFloat(lrSpeed);            // 写入左右速度
        out.writeFloat(udSpeed);            // 写入上下速度
        out.writeFloat(yaw);                // 写入偏航角
        out.writeShort(execTime);           // 写入执行时间
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/FlyPointControlCancelCmd.java
New file
@@ -0,0 +1,32 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.model.dto.manual.FlyPointControlCancelDTO;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
/**
 * 停止指点飞行指令
 *
 * @author liusuyi
 * @date 2024年10月26日15:43
 **/
public class FlyPointControlCancelCmd extends BaseCmd {
    public FlyPointControlCancelCmd(FlyPointControlCancelDTO flyPointControlCancelDTO) {
        setDataHeader((short) 0x7479);
        setDataLength((short) 18);
        setBoxSn(flyPointControlCancelDTO.getBoxSn().getBytes());
        setCmdCode((byte) 0xD1);
    }
    public void encode(ByteBuf out) {
        out.writeShort(getDataHeader());    // 写入数据头
        out.writeShort(getDataLength());    // 写入数据长度
        out.writeBytes(getBoxSn());         // 写入盒子SN
        out.writeByte(getCmdCode());        // 写入命令码
        out.writeByte(0x00);          // 写入加密标志
        out.writeByte(0x3A);          // 写入动作编号
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/FlyPointControlCmd.java
New file
@@ -0,0 +1,46 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.model.dto.manual.FlyPointControlDTO;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
/**
 * 指点飞行控制指令
 *
 * @author liusuyi
 * @date 2024年10月26日15:43
 **/
public class FlyPointControlCmd extends BaseCmd {
    private final double longitude;                  // 经度
    private final double latitude;                   // 纬度
    private final float altitude;                    // 高度
    private final float speed;                       // 速度
    private final byte flyMode;                      // 飞行模式
    public FlyPointControlCmd(FlyPointControlDTO flyPointControlDTO) {
        setDataHeader((short) 0x7479);
        setDataLength((short) 36);
        setBoxSn(flyPointControlDTO.getBoxSn().getBytes());
        setCmdCode((byte) 0xD1);
        this.longitude = flyPointControlDTO.getLongitude();
        this.latitude = flyPointControlDTO.getLatitude();
        this.altitude = flyPointControlDTO.getAltitude();
        this.speed = flyPointControlDTO.getSpeed();
        this.flyMode = flyPointControlDTO.getFlyMode();
    }
    public void encode(ByteBuf out) {
        out.writeShort(getDataHeader());    // 写入数据头
        out.writeShort(getDataLength());    // 写入数据长度
        out.writeBytes(getBoxSn());         // 写入盒子SN
        out.writeByte(getCmdCode());        // 写入命令码
        out.writeByte(0x00);          // 写入加密标志
        out.writeByte(0x39);          // 写入动作编号
        out.writeDouble(longitude);         // 写入经度
        out.writeDouble(latitude);          // 写入纬度
        out.writeFloat(altitude);           // 写入高度
        out.writeFloat(speed);              // 写入速度
        out.writeByte(flyMode);             // 写入飞行模式
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/LandCancelCmd.java
New file
@@ -0,0 +1,33 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.model.dto.manual.LandCancelDTO;
import com.ard.tianyu.model.dto.manual.LandDTO;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
/**
 * 取消降落指令
 *
 * @author liusuyi
 * @date 2024年10月26日15:43
 **/
public class LandCancelCmd extends BaseCmd {
    public LandCancelCmd(LandCancelDTO landCancleDTO) {
        setDataHeader((short) 0x7479);
        setDataLength((short) 18);
        setBoxSn(landCancleDTO.getBoxSn().getBytes());
        setCmdCode((byte) 0xD1);
    }
    public void encode(ByteBuf out) {
        out.writeShort(getDataHeader());    // 写入数据头
        out.writeShort(getDataLength());    // 写入数据长度
        out.writeBytes(getBoxSn());         // 写入盒子SN
        out.writeByte(getCmdCode());        // 写入命令码
        out.writeByte(0x00);          // 写入加密标志
        out.writeByte(0x15);          // 写入动作编号
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/LandCancelReplyCmd.java
New file
@@ -0,0 +1,33 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
import lombok.Data;
/**
 * 取消降落回复指令
 *
 * @author liusuyi
 * @date 2024年10月26日15:43
 **/
@Data
public class LandCancelReplyCmd extends BaseCmd {
    private final byte encryptFlag = 0x00; // 加密标志
    private final byte actionCode = 0x15;  // 动作编号
    private final byte result;             // 执行结果
    private final Integer errorCode;       // 错误码
    public LandCancelReplyCmd(String boxSn, Byte result, Integer errorCode) {
        setDataHeader((short) 0x6A77);
        setDataLength((short) 23);
        setBoxSn(boxSn.getBytes());
        setCmdCode((byte) 0xD1);
        this.result = result;
        this.errorCode = errorCode;
    }
    public void encode(ByteBuf out) {
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/LandCmd.java
New file
@@ -0,0 +1,33 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.model.dto.manual.LandDTO;
import com.ard.tianyu.model.dto.manual.ReturnHomeCancelDTO;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
/**
 * 降落指令
 *
 * @author liusuyi
 * @date 2024年10月26日15:43
 **/
public class LandCmd extends BaseCmd {
    public LandCmd(LandDTO landDTO) {
        setDataHeader((short) 0x7479);
        setDataLength((short) 18);
        setBoxSn(landDTO.getBoxSn().getBytes());
        setCmdCode((byte) 0xD1);
    }
    public void encode(ByteBuf out) {
        out.writeShort(getDataHeader());    // 写入数据头
        out.writeShort(getDataLength());    // 写入数据长度
        out.writeBytes(getBoxSn());         // 写入盒子SN
        out.writeByte(getCmdCode());        // 写入命令码
        out.writeByte(0x00);          // 写入加密标志
        out.writeByte(0x14);          // 写入动作编号
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/LandForcedCmd.java
New file
@@ -0,0 +1,33 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.model.dto.manual.LandCancelDTO;
import com.ard.tianyu.model.dto.manual.LandForcedDTO;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
/**
 * 强制降落指令
 *
 * @author liusuyi
 * @date 2024年10月26日15:43
 **/
public class LandForcedCmd extends BaseCmd {
    public LandForcedCmd(LandForcedDTO landForcedDTO) {
        setDataHeader((short) 0x7479);
        setDataLength((short) 18);
        setBoxSn(landForcedDTO.getBoxSn().getBytes());
        setCmdCode((byte) 0xD1);
    }
    public void encode(ByteBuf out) {
        out.writeShort(getDataHeader());    // 写入数据头
        out.writeShort(getDataLength());    // 写入数据长度
        out.writeBytes(getBoxSn());         // 写入盒子SN
        out.writeByte(getCmdCode());        // 写入命令码
        out.writeByte(0x00);          // 写入加密标志
        out.writeByte(0x29);          // 写入动作编号
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/LandForcedReplyCmd.java
New file
@@ -0,0 +1,33 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
import lombok.Data;
/**
 * 强制降落回复指令
 *
 * @author liusuyi
 * @date 2024年10月26日15:43
 **/
@Data
public class LandForcedReplyCmd extends BaseCmd {
    private final byte encryptFlag = 0x00; // 加密标志
    private final byte actionCode = 0x29;  // 动作编号
    private final byte result;             // 执行结果
    private final Integer errorCode;       // 错误码
    public LandForcedReplyCmd(String boxSn, Byte result, Integer errorCode) {
        setDataHeader((short) 0x6A77);
        setDataLength((short) 23);
        setBoxSn(boxSn.getBytes());
        setCmdCode((byte) 0xD1);
        this.result = result;
        this.errorCode = errorCode;
    }
    public void encode(ByteBuf out) {
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/LandReplyCmd.java
New file
@@ -0,0 +1,33 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
import lombok.Data;
/**
 * 降落回复指令
 *
 * @author liusuyi
 * @date 2024年10月26日15:43
 **/
@Data
public class LandReplyCmd extends BaseCmd {
    private final byte encryptFlag = 0x00; // 加密标志
    private final byte actionCode = 0x14;  // 动作编号
    private final byte result;             // 执行结果
    private final Integer errorCode;       // 错误码
    public LandReplyCmd(String boxSn, Byte result, Integer errorCode) {
        setDataHeader((short) 0x6A77);
        setDataLength((short) 23);
        setBoxSn(boxSn.getBytes());
        setCmdCode((byte) 0xD1);
        this.result = result;
        this.errorCode = errorCode;
    }
    public void encode(ByteBuf out) {
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/PointControlReachedReplyCmd.java
New file
@@ -0,0 +1,33 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
import lombok.Data;
/**
 * 到达指点飞行位置回复指令
 *
 * @author liusuyi
 * @date 2024年10月26日15:43
 **/
@Data
public class PointControlReachedReplyCmd extends BaseCmd {
    private final byte encryptFlag = 0x00; // 加密标志
    private final byte actionCode = 0x39;  // 动作编号
    private final byte result;             // 执行结果
    private final Integer errorCode;       // 错误码
    public PointControlReachedReplyCmd(String boxSn, Byte result, Integer errorCode) {
        setDataHeader((short) 0x6A77);
        setDataLength((short) 19);
        setBoxSn(boxSn.getBytes());
        setCmdCode((byte) 0xD1);
        this.result = result;
        this.errorCode = errorCode;
    }
    public void encode(ByteBuf out) {
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/ReturnHomeCancelCmd.java
New file
@@ -0,0 +1,33 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.model.dto.manual.ReturnHomeCancelDTO;
import com.ard.tianyu.model.dto.manual.ReturnHomeDTO;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
/**
 * 取消返航指令
 *
 * @author liusuyi
 * @date 2024年10月26日15:43
 **/
public class ReturnHomeCancelCmd extends BaseCmd {
    public ReturnHomeCancelCmd(ReturnHomeCancelDTO returnHomeCancelDTO) {
        setDataHeader((short) 0x7479);
        setDataLength((short) 18);
        setBoxSn(returnHomeCancelDTO.getBoxSn().getBytes());
        setCmdCode((byte) 0xD1);
    }
    public void encode(ByteBuf out) {
        out.writeShort(getDataHeader());    // 写入数据头
        out.writeShort(getDataLength());    // 写入数据长度
        out.writeBytes(getBoxSn());         // 写入盒子SN
        out.writeByte(getCmdCode());        // 写入命令码
        out.writeByte(0x00);          // 写入加密标志
        out.writeByte(0x13);          // 写入动作编号
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/command/manual/ReturnHomeCancelReplyCmd.java
New file
@@ -0,0 +1,33 @@
package com.ard.tianyu.netty.command.manual;
import com.ard.tianyu.netty.command.BaseCmd;
import io.netty.buffer.ByteBuf;
import lombok.Data;
/**
 * 取消返航回复指令
 *
 * @author liusuyi
 * @date 2024年10月26日15:43
 **/
@Data
public class ReturnHomeCancelReplyCmd extends BaseCmd {
    private final byte encryptFlag = 0x00; // 加密标志
    private final byte actionCode = 0x13;  // 动作编号
    private final byte result;             // 执行结果
    private final Integer errorCode;       // 错误码
    public ReturnHomeCancelReplyCmd(String boxSn, Byte result, Integer errorCode) {
        setDataHeader((short) 0x6A77);
        setDataLength((short) 23);
        setBoxSn(boxSn.getBytes());
        setCmdCode((byte) 0xD1);
        this.result = result;
        this.errorCode = errorCode;
    }
    public void encode(ByteBuf out) {
    }
}
ard-work/src/main/java/com/ard/tianyu/netty/handler/MessageProtocolDecoder.java
@@ -5,8 +5,7 @@
import com.ard.tianyu.netty.command.camera.CapturePicReplyCmd;
import com.ard.tianyu.netty.command.camera.RecordStartReplyCmd;
import com.ard.tianyu.netty.command.camera.RecordStopReplyCmd;
import com.ard.tianyu.netty.command.manual.ReturnHomeReplyCmd;
import com.ard.tianyu.netty.command.manual.TakeOffReplyCmd;
import com.ard.tianyu.netty.command.manual.*;
import com.ard.tianyu.netty.command.notice.BoxPowerStatusReplyCmd;
import com.ard.tianyu.netty.command.notice.FlyWayEndReplyCmd;
import com.ard.tianyu.netty.command.notice.UavReadyReplyCmd;
@@ -112,6 +111,60 @@
                        baseCmd = new ReturnHomeReplyCmd(boxSn, result, errorCode);
                        log.debug("返航回复: {}", boxSn);
                        break;
                    case 0x13:
                        result = in.readByte();
                        errorCode = in.readInt();
                        uavData = new byte[15];
                        in.readBytes(uavData);
                        boxSn = new String(uavData);
                        baseCmd = new ReturnHomeCancelReplyCmd(boxSn, result, errorCode);
                        log.debug("取消返航回复: {}", boxSn);
                        break;
                    case 0x14:
                        result = in.readByte();
                        errorCode = in.readInt();
                        uavData = new byte[15];
                        in.readBytes(uavData);
                        boxSn = new String(uavData);
                        baseCmd = new LandReplyCmd(boxSn, result, errorCode);
                        log.debug("降落回复: {}", boxSn);
                        break;
                    case 0x15:
                        result = in.readByte();
                        errorCode = in.readInt();
                        uavData = new byte[15];
                        in.readBytes(uavData);
                        boxSn = new String(uavData);
                        baseCmd = new LandCancelReplyCmd(boxSn, result, errorCode);
                        log.debug("取消降落回复: {}", boxSn);
                        break;
                    case 0x29:
                        result = in.readByte();
                        errorCode = in.readInt();
                        uavData = new byte[15];
                        in.readBytes(uavData);
                        boxSn = new String(uavData);
                        baseCmd = new LandForcedReplyCmd(boxSn, result, errorCode);
                        log.debug("强制降落回复: {}", boxSn);
                        break;
                    case 0x32:
                        result = in.readByte();
                        errorCode = in.readInt();
                        uavData = new byte[15];
                        in.readBytes(uavData);
                        boxSn = new String(uavData);
                        baseCmd = new EmergencyBrakeReplyCmd(boxSn, result, errorCode);
                        log.debug("紧急制动回复: {}", boxSn);
                        break;
                    case 0x39:
                        result = in.readByte();
                        errorCode = in.readInt();
                        uavData = new byte[15];
                        in.readBytes(uavData);
                        boxSn = new String(uavData);
                        baseCmd = new PointControlReachedReplyCmd(boxSn, result, errorCode);
                        log.debug("到达指点飞行位置回复: {}", boxSn);
                        break;
                    case 0x22:
                        result = in.readByte();
                        errorCode = in.readInt();
ard-work/src/main/java/com/ard/tianyu/service/ITyService.java
@@ -7,9 +7,7 @@
import com.ard.tianyu.model.dto.control.AbsAngleControlDTO;
import com.ard.tianyu.model.dto.control.AngleControlDTO;
import com.ard.tianyu.model.dto.control.SpeedControlDTO;
import com.ard.tianyu.model.dto.manual.ReturnHomeDTO;
import com.ard.tianyu.model.dto.manual.SwitchUavControlDTO;
import com.ard.tianyu.model.dto.manual.TakeOffDTO;
import com.ard.tianyu.model.dto.manual.*;
import com.ard.tianyu.model.dto.signal.SignalInfo;
import com.ard.tianyu.model.dto.state.UavState;
import com.ard.tianyu.model.dto.telemetry.TelemetryData;
@@ -138,6 +136,47 @@
     * 返航
     */
    void returnHome(ReturnHomeDTO returnHomeDTO);
    /**
     * 取消返航
     */
    void returnHomeCancel(ReturnHomeCancelDTO returnHomeCancelDTO);
    /**
     * 降落
     */
    void land(LandDTO landDTO);
    /**
     * 取消降落
     */
    void landCancel(LandCancelDTO landCancelDTO);
    /**
     * 强制降落
     */
    void landForced(LandForcedDTO landForcedDTO);
    /**
     * 方向控制
     */
    void flyControl(FlyControlDTO flyControlDTO);
    /**
     * 指点飞行控制
     */
    void flyPointControl(FlyPointControlDTO flyPointControlDTO);
    /**
     * 停止指点飞行控制
     */
    void flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO);
    /**
     * 紧急制动
     */
    void emeragencyBrake(EmeragencyBrakeDTO emeragencyBrakeDTO);
    /**
     * 获取无人机状态
     */
ard-work/src/main/java/com/ard/tianyu/service/impl/TyServiceImpl.java
@@ -15,9 +15,7 @@
import com.ard.tianyu.model.dto.converter.UavSignalInfoConverter;
import com.ard.tianyu.model.dto.converter.UavStateConverter;
import com.ard.tianyu.model.dto.converter.UavTelemetryDataConverter;
import com.ard.tianyu.model.dto.manual.ReturnHomeDTO;
import com.ard.tianyu.model.dto.manual.SwitchUavControlDTO;
import com.ard.tianyu.model.dto.manual.TakeOffDTO;
import com.ard.tianyu.model.dto.manual.*;
import com.ard.tianyu.model.dto.signal.SignalInfo;
import com.ard.tianyu.model.dto.state.UavState;
import com.ard.tianyu.model.dto.telemetry.TelemetryData;
@@ -38,6 +36,7 @@
import com.ard.tianyu.netty.command.register.*;
import com.ard.tianyu.netty.handler.ErrorCodeEnum;
import com.ard.tianyu.service.ITyService;
import com.ard.tianyu.websocket.utils.WebSocketUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import lombok.extern.slf4j.Slf4j;
@@ -300,6 +299,78 @@
    }
    /**
     * 取消返航
     */
    @Override
    public void returnHomeCancel(ReturnHomeCancelDTO returnHomeCancelDTO) {
        ReturnHomeCancelCmd returnHomeCancelCmd = new ReturnHomeCancelCmd(returnHomeCancelDTO);
        this.sendMessage(tyConfiguration.getIpPort(), returnHomeCancelCmd);
    }
    /**
     * 降落
     */
    @Override
    public void land(LandDTO landDTO) {
        LandCmd landCmd = new LandCmd(landDTO);
        this.sendMessage(tyConfiguration.getIpPort(), landCmd);
    }
    /**
     * 取消降落
     */
    @Override
    public void landCancel(LandCancelDTO landCancelDTO) {
        LandCancelCmd landCancelCmd = new LandCancelCmd(landCancelDTO);
        this.sendMessage(tyConfiguration.getIpPort(), landCancelCmd);
    }
    /**
     * 强制降落
     */
    @Override
    public void landForced(LandForcedDTO landForcedDTO) {
        LandForcedCmd landForcedCmd = new LandForcedCmd(landForcedDTO);
        this.sendMessage(tyConfiguration.getIpPort(), landForcedCmd);
    }
    /**
     * 方向控制
     */
    @Override
    public void flyControl(FlyControlDTO flyControlDTO) {
        FlyControlCmd flyControlCmd = new FlyControlCmd(flyControlDTO);
        this.sendMessage(tyConfiguration.getIpPort(), flyControlCmd);
    }
    /**
     * 指点飞行控制
     */
    @Override
    public void flyPointControl(FlyPointControlDTO flyPointControlDTO) {
        FlyPointControlCmd flyPointControlCmd = new FlyPointControlCmd(flyPointControlDTO);
        this.sendMessage(tyConfiguration.getIpPort(), flyPointControlCmd);
    }
    /**
     * 停止指点飞行控制
     */
    @Override
    public void flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO) {
        FlyPointControlCancelCmd flyPointControlCancelCmd = new FlyPointControlCancelCmd(flyPointControlCancelDTO);
        this.sendMessage(tyConfiguration.getIpPort(), flyPointControlCancelCmd);
    }
    /**
     * 紧急制动
     */
    @Override
    public void emeragencyBrake(EmeragencyBrakeDTO emeragencyBrakeDTO) {
        EmergencyBrakeCmd emergencyBrakeCmd=new EmergencyBrakeCmd(emeragencyBrakeDTO);
        this.sendMessage(tyConfiguration.getIpPort(), emergencyBrakeCmd);
    }
    /**
     * 获取无人机状态
     */
    @Override
@@ -362,6 +433,7 @@
                UavState uavState = UavStateConverter.convertProtoToEntity(uavStateCmd.getUavState());
                UavStateCache.putUavState(uavState);
                logPrint("无人机状态", uavState.getBoxSn());
                WebSocketUtil.sendMessageAll(uavState);
                break;
            case (byte) 0xA9:
                // 处理无人机遥测消息
@@ -419,6 +491,36 @@
                    ReturnHomeReplyCmd returnHomeReplyCmd = (ReturnHomeReplyCmd) tyCommand;
                    logPrint("无人机返航", returnHomeReplyCmd.getResult(), returnHomeReplyCmd.getErrorCode());
                }
                //取消返航回复
                if (tyCommand instanceof ReturnHomeCancelReplyCmd) {
                    ReturnHomeCancelReplyCmd returnHomeCancelReplyCmd = (ReturnHomeCancelReplyCmd) tyCommand;
                    logPrint("取消无人机返航", returnHomeCancelReplyCmd.getResult(), returnHomeCancelReplyCmd.getErrorCode());
                }
                //降落回复
                if (tyCommand instanceof LandReplyCmd) {
                    LandReplyCmd landReplyCmd = (LandReplyCmd) tyCommand;
                    logPrint("降落", landReplyCmd.getResult(), landReplyCmd.getErrorCode());
                }
                //取消降落回复
                if (tyCommand instanceof LandCancelReplyCmd) {
                    LandCancelReplyCmd landCancelReplyCmd = (LandCancelReplyCmd) tyCommand;
                    logPrint("取消降落", landCancelReplyCmd.getResult(), landCancelReplyCmd.getErrorCode());
                }
                //强制降落回复
                if (tyCommand instanceof LandForcedReplyCmd) {
                    LandForcedReplyCmd landForcedReplyCmd = (LandForcedReplyCmd) tyCommand;
                    logPrint("强制降落", landForcedReplyCmd.getResult(), landForcedReplyCmd.getErrorCode());
                }
                //到达指点飞行位置回复
                if (tyCommand instanceof PointControlReachedReplyCmd) {
                    PointControlReachedReplyCmd pointControlReachedReplyCmd = (PointControlReachedReplyCmd) tyCommand;
                    logPrint("到达指点飞行位置", pointControlReachedReplyCmd.getResult(), pointControlReachedReplyCmd.getErrorCode());
                }
                //紧急制动回复
                if (tyCommand instanceof EmergencyBrakeReplyCmd) {
                    EmergencyBrakeReplyCmd emergencyBrakeReplyCmd = (EmergencyBrakeReplyCmd) tyCommand;
                    logPrint("紧急制动回复", emergencyBrakeReplyCmd.getResult(), emergencyBrakeReplyCmd.getErrorCode());
                }
                break;
            default:
                // 处理其他消息
@@ -430,6 +532,7 @@
    private void logPrint(String message, Object obj) {
        log.debug("收到{}回复{}", message, obj);
    }
    //公共日志打印
    private void logPrint(String message, byte result, Integer errorCode) {
        log.info("收到{}回复:{} 错误码:{}【{}】",
ard-work/src/main/java/com/ard/tianyu/websocket/config/WebSocketConfig.java
New file
@@ -0,0 +1,27 @@
package com.ard.tianyu.websocket.config;
import com.ard.tianyu.websocket.handler.TyMessageHandler;
import com.ard.tianyu.websocket.interceptor.TyHandshakeInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import javax.annotation.Resource;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Resource
    private TyMessageHandler tyMessageHandler;
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        //添加myWebSocketHandler消息处理对象以及websocket连接地址
        registry.addHandler(tyMessageHandler, "/ws/**")
                //设置允许跨域访问
                .setAllowedOrigins("*")
                //添加拦截器可实现用户链接前进行权限校验等操作
                .addInterceptors(new TyHandshakeInterceptor());
    }
}
ard-work/src/main/java/com/ard/tianyu/websocket/handler/TyMessageHandler.java
New file
@@ -0,0 +1,39 @@
package com.ard.tianyu.websocket.handler;
import com.ard.tianyu.websocket.utils.WebSocketUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
@Component
@Slf4j
public class TyMessageHandler extends TextWebSocketHandler {
    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 处理接收到的消息
        String payload = message.getPayload();
        log.info("【websocket】Received message: " + payload);
        log.info("【websocket】size: " + WebSocketUtil.ONLINE_USER_SESSIONS.size());
    }
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        log.info("【websocket】Connected: " + session.getId());
        String userId = (String) session.getAttributes().get("userId");
        WebSocketUtil.ONLINE_USER_SESSIONS.put(userId, session);
        log.info("【websocket】size: " + WebSocketUtil.ONLINE_USER_SESSIONS.size());
    }
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        log.info("【websocket】Disconnected: " + session.getId());
        String userId = (String) session.getAttributes().get("userId");
        WebSocketUtil.ONLINE_USER_SESSIONS.remove(userId);
        log.info("【websocket】size: " + WebSocketUtil.ONLINE_USER_SESSIONS.size());
    }
}
ard-work/src/main/java/com/ard/tianyu/websocket/interceptor/TyHandshakeInterceptor.java
New file
@@ -0,0 +1,35 @@
package com.ard.tianyu.websocket.interceptor;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import java.util.Map;
public class TyHandshakeInterceptor implements HandshakeInterceptor {
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
                                   WebSocketHandler wsHandler, Map<String, Object> attributes){
        //从路径中获取userId
        String path = request.getURI().getPath();
        String[] segments = path.split("/");
        if (segments.length > 2) {
            String userId = segments[segments.length - 1];
            attributes.put("userId", userId);
        }
        //从header中获取userId
//        String userId = request.getHeaders().getFirst("userId");
//        if (userId != null)
//        {
//            attributes.put("userId", userId);
//        }
        return true;
    }
    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
                               WebSocketHandler wsHandler, Exception ex) {
    }
}
ard-work/src/main/java/com/ard/tianyu/websocket/utils/WebSocketUtil.java
New file
@@ -0,0 +1,141 @@
package com.ard.tianyu.websocket.utils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * @ClassName WebSocketUtils
 * @Description: websocket工具类
 * @Author 刘苏义
 * @Date 2023/1/27 7:46
 * @Version 1.0
 */
@Slf4j
public final class WebSocketUtil {
    // 存储 websocket session
    public static final ConcurrentMap<String, WebSocketSession> ONLINE_USER_SESSIONS = new ConcurrentHashMap<>();
    /**
     * @param session 用户 session
     * @param message 发送内容
     */
    public static void sendMessage(WebSocketSession session, String message) {
        if (session == null) {
            return;
        }
        final InetSocketAddress remoteAddress = session.getRemoteAddress();
        if (remoteAddress == null) {
            return;
        }
        synchronized (session) {
            try {
                log.debug("发送消息:" + message);
                session.sendMessage(new TextMessage(String.join(", ", message)));
            } catch (IOException e) {
                log.error("sendMessage IOException ", e);
            }
        }
    }
    /**
     * @param session 用户 session
     * @param message 发送内容
     */
    public static void sendMessage(WebSocketSession session, Map message) {
        if (session == null) {
            return;
        }
       final InetSocketAddress remoteAddress = session.getRemoteAddress();
        if (remoteAddress == null) {
            return;
        }
        synchronized (session) {
            try {
                session.sendMessage(new TextMessage(JSONObject.toJSONString(message)));
            } catch (IOException e) {
                log.error("sendMessage IOException ", e);
            }
        }
    }
    public static void sendMessage(WebSocketSession session, List message) {
        if (session == null) {
            return;
        }
        final InetSocketAddress remoteAddress = session.getRemoteAddress();
        if (remoteAddress == null) {
            return;
        }
        synchronized (session) {
            try {
                session.sendMessage(new TextMessage(JSONObject.toJSONString(message)));
            } catch (IOException e) {
                log.error("sendMessage IOException ", e);
            }
        }
    }
    /**
     * 推送消息到其他客户端
     *
     * @param message
     */
    public static void sendMessageAll(String message) {
        ONLINE_USER_SESSIONS.forEach((sessionId, session) -> sendMessage(session, message));
    }
    /**
     * 推送消息到其他客户端
     *
     * @param message
     */
    public static void sendMessageAll(Map message) {
        JSONObject jsonObject = new JSONObject(message);
        ONLINE_USER_SESSIONS.forEach((sessionId, session) -> sendMessage(session, jsonObject.toString()));
    }
    /**
     * 推送消息到其他客户端
     *
     * @param message
     */
    public static void sendMessageAll(Object message) {
        String jsonMessage = JSONObject.toJSONString(message);  // 确保对象转换为 JSON 字符串
        ONLINE_USER_SESSIONS.forEach((sessionId, session) -> sendMessage(session, jsonMessage));
    }
    /**
     * 推送消息到其他客户端
     *
     * @param message
     */
    public static void sendMessageAll(List message) {
        ONLINE_USER_SESSIONS.forEach((sessionId, session) -> sendMessage(session, JSON.toJSONString(message)));
    }
    /**
     * @Author 刘苏义
     * @Description  发送消息给userId_前缀的人
     * @Date   2024/7/16 10:24
     * @Param
     * @return
     */
    public static void sendMessagePrefix(String targetId, String message) {
        String regex = "^" + Pattern.quote(targetId) + "_\\d+$";
        Pattern pattern = Pattern.compile(regex);
        ONLINE_USER_SESSIONS.entrySet().stream().filter(entry -> {
            Matcher matcher = pattern.matcher(entry.getKey());
            return matcher.matches();
        }).map(Map.Entry::getValue).forEach(session -> WebSocketUtil.sendMessage(session, message));
    }
}