liusuyi
2025-02-20 35f1a08b31531d971169403978cddf3328accb26
添加无人机控制权申请、处理、释放
8 files added
9 files modified
465 ■■■■■ changed files
ard-admin/src/main/resources/application.yml 2 ●●●●● patch | view | raw | blame | history
ard-common/src/main/java/com/ard/common/exception/NoPermissionException.java 7 ●●●●● patch | view | raw | blame | history
ard-framework/src/main/java/com/ard/framework/web/exception/GlobalExceptionHandler.java 11 ●●●●● patch | view | raw | blame | history
ard-work/pom.xml 4 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/aspect/ControlAuthorityAspect.java 89 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/aspect/annotation/CheckControlAuthority.java 9 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/controller/UavPermissionsController.java 57 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/service/ITyService.java 52 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/service/impl/TyServiceImpl.java 80 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/service/impl/UavControlManager.java 92 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/websocket/config/WebSocketConfig.java 2 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/websocket/domain/ControlRequestMessage.java 19 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/websocket/domain/WsMessage.java 13 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/websocket/handler/TyMessageHandler.java 3 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/websocket/handler/TyStreamHandler.java 1 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/websocket/utils/WebSocketUtil.java 5 ●●●●● patch | view | raw | blame | history
ard-work/src/main/java/com/ard/tianyu/websocket/utils/WsMessageConverter.java 19 ●●●●● patch | view | raw | blame | history
ard-admin/src/main/resources/application.yml
@@ -117,7 +117,7 @@
  # redis 配置
  redis:
    # 地址
    host: 192.168.1.15
    host: 127.0.0.1
    # 端口,默认为6379
    port: 6379
    # 数据库索引
ard-common/src/main/java/com/ard/common/exception/NoPermissionException.java
New file
@@ -0,0 +1,7 @@
package com.ard.common.exception;
public class NoPermissionException extends RuntimeException {
    public NoPermissionException(String message) {
        super(message);
    }
}
ard-framework/src/main/java/com/ard/framework/web/exception/GlobalExceptionHandler.java
@@ -1,6 +1,8 @@
package com.ard.framework.web.exception;
import javax.servlet.http.HttpServletRequest;
import com.ard.common.exception.NoPermissionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
@@ -142,4 +144,13 @@
    {
        return AjaxResult.error("演示模式,不允许操作");
    }
    /**
     * 无人机无控制权异常
     */
    @ExceptionHandler(NoPermissionException.class)
    public AjaxResult handleNoPermissionException(NoPermissionException e)
    {
        return AjaxResult.error(e.getMessage());
    }
}
ard-work/pom.xml
@@ -62,6 +62,10 @@
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
    </dependencies>
</project>
ard-work/src/main/java/com/ard/tianyu/aspect/ControlAuthorityAspect.java
New file
@@ -0,0 +1,89 @@
package com.ard.tianyu.aspect;
import com.ard.common.exception.NoPermissionException;
import com.ard.common.utils.SecurityUtils;
import com.ard.tianyu.service.impl.UavControlManager;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
 * 控制权限切面
 *
 * @author liusuyi
 */
@Aspect
@Component
@Slf4j
public class ControlAuthorityAspect {
    @Pointcut("@annotation(com.ard.tianyu.aspect.annotation.CheckControlAuthority)")
    public void dsPointCut() {
    }
    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Boolean result = controlScopeFilter(point);
        if (result) {
            log.debug("已获取无人机控制权--{}", method.getName());
            // 执行目标方法
            return point.proceed();
        }
        log.debug("未获取无人机控制权--{}", method.getName());
        throw new NoPermissionException("未获取无人机控制权");
    }
    /**
     * 操控判断逻辑
     *
     * @param joinPoint 切点
     */
    public Boolean controlScopeFilter(ProceedingJoinPoint joinPoint) {
        // 获取方法参数
        Object[] args = joinPoint.getArgs();
        // 使用反射获取参数中的 boxSn
        for (Object arg : args) {
            String boxSn = getBoxSn(arg);
            if (boxSn != null) {
                //判断无人机控制者是否为当前登录用户
                Long currentControllerId = UavControlManager.droneControlMap.get(boxSn);
                if (currentControllerId != null && currentControllerId.equals(SecurityUtils.getUserId())) {
                    return true;
                }
            }
        }
        return false;
    }
    // 通过反射获取对象中的无人机boxSn字段
    public static String getBoxSn(Object dto) {
        try {
            // 获取类的所有字段
            Field[] fields = dto.getClass().getDeclaredFields();
            for (Field field : fields) {
                // 设置字段可访问
                field.setAccessible(true);
                // 判断是否是 boxSn 字段
                if ("boxSn".equals(field.getName())) {
                    // 获取并返回 droneId 的值
                    return (String) field.get(dto);
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null; // 如果没有找到 droneId 字段,返回 null
    }
}
ard-work/src/main/java/com/ard/tianyu/aspect/annotation/CheckControlAuthority.java
New file
@@ -0,0 +1,9 @@
package com.ard.tianyu.aspect.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckControlAuthority {
}
ard-work/src/main/java/com/ard/tianyu/controller/UavPermissionsController.java
New file
@@ -0,0 +1,57 @@
package com.ard.tianyu.controller;
import com.ard.common.core.domain.AjaxResult;
import com.ard.common.utils.SecurityUtils;
import com.ard.tianyu.service.impl.UavControlManager;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
 * @ClassName: UavPermissionsController
 * @Description: 无人机权限控制器
 * @Author: liusuyi
 * @Date: 2025年02月20日9:38
 **/
@Api(tags = "无人机权限控制接口")
@RequestMapping("/uav/permissions")
@RestController
public class UavPermissionsController {
    @Resource
    private UavControlManager uavControlManager;
    @ApiOperation("申请控制")
    @ApiOperationSupport(order = 1)
    @GetMapping("/requestControl")
    public AjaxResult requestControl(String boxSn) {
        return AjaxResult.success(uavControlManager.requestControl(boxSn));
    }
    @ApiOperation("处理申请")
    @ApiOperationSupport(order = 2)
    @GetMapping("/handleControlResponse")
    public AjaxResult handleControlResponse(String boxSn, Boolean approve) {
        return AjaxResult.success(uavControlManager.handleControlResponse(boxSn, approve));
    }
    @ApiOperation("释放控制")
    @ApiOperationSupport(order = 3)
    @GetMapping("/releaseControl")
    public AjaxResult releaseControl(String boxSn) {
        return AjaxResult.success(uavControlManager.releaseControl(boxSn));
    }
    @ApiOperation("获取无人机控制者")
    @ApiOperationSupport(order = 4)
    @GetMapping("/controlRequestMap")
    public AjaxResult controlRequestMap(String boxSn) {
        return AjaxResult.success(UavControlManager.droneControlMap.get(boxSn));
    }
}
ard-work/src/main/java/com/ard/tianyu/service/ITyService.java
@@ -72,134 +72,134 @@
     * 设置返航高度
     * @return
     */
    void updateReturnHeight(ReturnHeightSettingDTO returnHeightSettingDTO);
    Boolean updateReturnHeight(ReturnHeightSettingDTO returnHeightSettingDTO);
    /**
     * 设置视频码流
     * @return
     */
    void updateVideoStream(VideoStreamSettingDTO videoStreamSettingDTO);
    Boolean updateVideoStream(VideoStreamSettingDTO videoStreamSettingDTO);
    /**
     * 设置返航点
     * @return
     */
    void updateReturnPoint(ReturnPointSettingDTO returnPointSettingDTO);
    Boolean updateReturnPoint(ReturnPointSettingDTO returnPointSettingDTO);
    /**
     * 注册
     */
    void registerMessage();
    Boolean registerMessage();
    /**
     * 云台转动(基于角度)
     */
    void angleControl(AngleControlDTO angleControlDto);
    Boolean angleControl(AngleControlDTO angleControlDto);
    /**
     * 云台绝对值控制(基于角度)
     */
    void absAngleControl(AbsAngleControlDTO absAngleControlDto);
    Boolean absAngleControl(AbsAngleControlDTO absAngleControlDto);
    /**
     * 云台转动(基于速度)
     */
    void speedControl(SpeedControlDTO speedControlDto);
    Boolean speedControl(SpeedControlDTO speedControlDto);
    /**
     * 设置相机模式
     */
    void updateCameraMode(CameraModeDTO cameraModeDTO);
    Boolean updateCameraMode(CameraModeDTO cameraModeDTO);
    /**
     * 拍照
     */
    void capturePic(CapturePicDTO capturePicDto);
    Boolean capturePic(CapturePicDTO capturePicDto);
    /**
     * 录像
     */
    void record(RecordDTO recordDto);
    Boolean record(RecordDTO recordDto);
    /**
     * 变倍
     */
    void zoom(ZoomDTO zoomDTO);
    Boolean zoom(ZoomDTO zoomDTO);
    /**
     * 变倍到指定倍数
     */
    void zoomToLevel(ZoomToLevelDTO zoomToLevelDTO);
    Boolean zoomToLevel(ZoomToLevelDTO zoomToLevelDTO);
    /**
     * 切换摄像头
     */
    void switchCamera(SwitchCameraDTO switchCameraDTO);
    Boolean switchCamera(SwitchCameraDTO switchCameraDTO);
    /**
     * 切换视频源
     */
    void switchSource(SwitchSourceDTO switchSourceDTO);
    Boolean switchSource(SwitchSourceDTO switchSourceDTO);
    /**
     * 打开或关闭照片回传
     */
    void photoReturnToggle(PhotoReturnToggleDTO photoReturnToggleDTO);
    Boolean photoReturnToggle(PhotoReturnToggleDTO photoReturnToggleDTO);
    /**
     * 切换无人机控制权
     */
    void switchUavControl(SwitchUavControlDTO switchUavControlDTO);
    Boolean switchUavControl(SwitchUavControlDTO switchUavControlDTO);
    /**
     * 起飞
     */
    void takeOff(TakeOffDTO takeOffDTO);
    Boolean takeOff(TakeOffDTO takeOffDTO);
    /**
     * 返航
     */
    void returnHome(ReturnHomeDTO returnHomeDTO);
    Boolean returnHome(ReturnHomeDTO returnHomeDTO);
    /**
     * 取消返航
     */
    void returnHomeCancel(ReturnHomeCancelDTO returnHomeCancelDTO);
    Boolean returnHomeCancel(ReturnHomeCancelDTO returnHomeCancelDTO);
    /**
     * 降落
     */
    void land(LandDTO landDTO);
    Boolean land(LandDTO landDTO);
    /**
     * 取消降落
     */
    void landCancel(LandCancelDTO landCancelDTO);
    Boolean landCancel(LandCancelDTO landCancelDTO);
    /**
     * 强制降落
     */
    void landForced(LandForcedDTO landForcedDTO);
    Boolean landForced(LandForcedDTO landForcedDTO);
    /**
     * 方向控制
     */
    void flyControl(FlyControlDTO flyControlDTO);
    Boolean flyControl(FlyControlDTO flyControlDTO);
    /**
     * 指点飞行控制
     */
    void flyPointControl(FlyPointControlDTO flyPointControlDTO);
    Boolean flyPointControl(FlyPointControlDTO flyPointControlDTO);
    /**
     * 停止指点飞行控制
     */
    void flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO);
    Boolean flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO);
    /**
     * 紧急制动
     */
    void emeragencyBrake(EmeragencyBrakeDTO emeragencyBrakeDTO);
    Boolean emeragencyBrake(EmeragencyBrakeDTO emeragencyBrakeDTO);
    /**
     * 获取无人机状态
ard-work/src/main/java/com/ard/tianyu/service/impl/TyServiceImpl.java
@@ -1,6 +1,7 @@
package com.ard.tianyu.service.impl;
import com.ard.common.utils.ByteUtils;
import com.ard.tianyu.aspect.annotation.CheckControlAuthority;
import com.ard.tianyu.config.TyConfiguration;
import com.ard.tianyu.http.TyClient;
import com.ard.tianyu.model.cache.TyTcpServerCache;
@@ -155,34 +156,37 @@
     * 设置返航高度
     */
    @Override
    public void updateReturnHeight(ReturnHeightSettingDTO returnHeightSettingDTO) {
    public Boolean updateReturnHeight(ReturnHeightSettingDTO returnHeightSettingDTO) {
        ReturnHeightSettingCmd returnHeightSettingCmd = new ReturnHeightSettingCmd(returnHeightSettingDTO);
        this.sendMessage(tyConfiguration.getIpPort(), returnHeightSettingCmd);
        return true;
    }
    /**
     * 设置返航点
     */
    @Override
    public void updateReturnPoint(ReturnPointSettingDTO returnPointSettingDTO) {
    public Boolean updateReturnPoint(ReturnPointSettingDTO returnPointSettingDTO) {
        ReturnPointSettingCmd returnPointSettingCmd = new ReturnPointSettingCmd(returnPointSettingDTO);
        this.sendMessage(tyConfiguration.getIpPort(), returnPointSettingCmd);
        return true;
    }
    /**
     * 设置视频码流
     */
    @Override
    public void updateVideoStream(VideoStreamSettingDTO videoStreamSettingDTO) {
    public Boolean updateVideoStream(VideoStreamSettingDTO videoStreamSettingDTO) {
        VideoStreamSettingCmd videoStreamSettingCmd = new VideoStreamSettingCmd(videoStreamSettingDTO);
        this.sendMessage(tyConfiguration.getIpPort(), videoStreamSettingCmd);
        return true;
    }
    /**
     * 注册天宇平台
     */
    @Override
    public void registerMessage() {
    public Boolean registerMessage() {
        try {
            AccessTokenReceiver tyToken = tyConfiguration.getAccessToken();
            RegisterCmd registerMessage = new RegisterCmd(tyToken.getCompanyId(), tyToken.getAccessToken().getBytes());
@@ -190,59 +194,65 @@
        } catch (Exception e) {
            log.error("注册天宇平台失败:{}", e.getMessage());
        }
        return true;
    }
    /**
     * 云台控制基于角度
     */
    @Override
    public void angleControl(AngleControlDTO angleControlDto) {
    public Boolean angleControl(AngleControlDTO angleControlDto) {
        AngleControlCmd controlAngleCommand = new AngleControlCmd(angleControlDto);
        this.sendMessage(tyConfiguration.getIpPort(), controlAngleCommand);
        return true;
    }
    /**
     * 云台绝对值控制(基于角度)
     */
    @Override
    public void absAngleControl(AbsAngleControlDTO absAngleControlDto) {
    public Boolean absAngleControl(AbsAngleControlDTO absAngleControlDto) {
        AbsAngleControlCmd absAngleControlCommand = new AbsAngleControlCmd(absAngleControlDto);
        this.sendMessage(tyConfiguration.getIpPort(), absAngleControlCommand);
        return true;
    }
    /**
     * 云台转动(基于速度)
     */
    @Override
    public void speedControl(SpeedControlDTO speedControlDto) {
    public Boolean speedControl(SpeedControlDTO speedControlDto) {
        SpeedControlCmd speedControlCommand = new SpeedControlCmd(speedControlDto);
        this.sendMessage(tyConfiguration.getIpPort(), speedControlCommand);
        return true;
    }
    /**
     * 设置相机模式
     */
    @Override
    public void updateCameraMode(CameraModeDTO cameraModeDTO) {
    public Boolean updateCameraMode(CameraModeDTO cameraModeDTO) {
        CameraModeCmd cameraModeCommand = new CameraModeCmd(cameraModeDTO);
        this.sendMessage(tyConfiguration.getIpPort(), cameraModeCommand);
        return true;
    }
    /**
     * 拍照
     */
    @Override
    public void capturePic(CapturePicDTO capturePicDto) {
    public Boolean capturePic(CapturePicDTO capturePicDto) {
        updateCameraMode(CameraModeDTO.builder().boxSn(capturePicDto.getBoxSn()).actionParam((byte) 0x01).build());
        CapturePicCmd capturePicCommand = new CapturePicCmd(capturePicDto);
        this.sendMessage(tyConfiguration.getIpPort(), capturePicCommand);
        return true;
    }
    /**
     * 录像
     */
    @Override
    public void record(RecordDTO recordDto) {
    public Boolean record(RecordDTO recordDto) {
        BaseCmd baseCmd;
        if (recordDto.getIsRecord()) {
            updateCameraMode(CameraModeDTO.builder().boxSn(recordDto.getBoxSn()).actionParam((byte) 0x02).build());
@@ -251,150 +261,168 @@
            baseCmd = new RecordStopCmd(recordDto);
        }
        this.sendMessage(tyConfiguration.getIpPort(), baseCmd);
        return true;
    }
    /**
     * 变倍
     */
    @Override
    public void zoom(ZoomDTO zoomDTO) {
    public Boolean zoom(ZoomDTO zoomDTO) {
        ZoomCmd zoomCommand = new ZoomCmd(zoomDTO);
        this.sendMessage(tyConfiguration.getIpPort(), zoomCommand);
        return true;
    }
    /**
     * 变倍到指定倍数
     */
    @Override
    public void zoomToLevel(ZoomToLevelDTO zoomToLevelDTO) {
    public Boolean zoomToLevel(ZoomToLevelDTO zoomToLevelDTO) {
        ZoomToLevelCmd zoomToLevelCommand = new ZoomToLevelCmd(zoomToLevelDTO);
        this.sendMessage(tyConfiguration.getIpPort(), zoomToLevelCommand);
        return true;
    }
    /**
     * 切换摄像头
     */
    @Override
    public void switchCamera(SwitchCameraDTO switchCameraDTO) {
    public Boolean switchCamera(SwitchCameraDTO switchCameraDTO) {
        SwitchCameraCmd switchCameraCmd = new SwitchCameraCmd(switchCameraDTO);
        this.sendMessage(tyConfiguration.getIpPort(), switchCameraCmd);
        return true;
    }
    /**
     * 切换视频源
     */
    @Override
    public void switchSource(SwitchSourceDTO switchSourceDTO) {
    public Boolean switchSource(SwitchSourceDTO switchSourceDTO) {
        SwitchSourceCmd switchSourceCmd = new SwitchSourceCmd(switchSourceDTO);
        this.sendMessage(tyConfiguration.getIpPort(), switchSourceCmd);
        return true;
    }
    /**
     * 打开或关闭照片回传
     */
    @Override
    public void photoReturnToggle(PhotoReturnToggleDTO photoReturnToggleDTO) {
    public Boolean photoReturnToggle(PhotoReturnToggleDTO photoReturnToggleDTO) {
        PhotoReturnToggleCmd photoReturnToggleCmd = new PhotoReturnToggleCmd(photoReturnToggleDTO);
        this.sendMessage(tyConfiguration.getIpPort(), photoReturnToggleCmd);
        return true;
    }
    /**
     * 切换无人机控制权
     */
    @Override
    public void switchUavControl(SwitchUavControlDTO switchUavControlDTO) {
    public Boolean switchUavControl(SwitchUavControlDTO switchUavControlDTO) {
        SwitchUavControlCmd switchUavControlCmd = new SwitchUavControlCmd(switchUavControlDTO);
        this.sendMessage(tyConfiguration.getIpPort(), switchUavControlCmd);
        return true;
    }
    /**
     * 起飞
     */
    @CheckControlAuthority
    @Override
    public void takeOff(TakeOffDTO takeOffDTO) {
    public Boolean takeOff(TakeOffDTO takeOffDTO) {
        TakeOffCmd takeOffCmd = new TakeOffCmd(takeOffDTO);
        this.sendMessage(tyConfiguration.getIpPort(), takeOffCmd);
        return true;
    }
    /**
     * 返航
     */
    @Override
    public void returnHome(ReturnHomeDTO returnHomeDTO) {
    public Boolean returnHome(ReturnHomeDTO returnHomeDTO) {
        ReturnHomeCmd returnHomeCmd = new ReturnHomeCmd(returnHomeDTO);
        this.sendMessage(tyConfiguration.getIpPort(), returnHomeCmd);
        return true;
    }
    /**
     * 取消返航
     */
    @Override
    public void returnHomeCancel(ReturnHomeCancelDTO returnHomeCancelDTO) {
    public Boolean returnHomeCancel(ReturnHomeCancelDTO returnHomeCancelDTO) {
        ReturnHomeCancelCmd returnHomeCancelCmd = new ReturnHomeCancelCmd(returnHomeCancelDTO);
        this.sendMessage(tyConfiguration.getIpPort(), returnHomeCancelCmd);
        return true;
    }
    /**
     * 降落
     */
    @Override
    public void land(LandDTO landDTO) {
    public Boolean land(LandDTO landDTO) {
        LandCmd landCmd = new LandCmd(landDTO);
        this.sendMessage(tyConfiguration.getIpPort(), landCmd);
        return true;
    }
    /**
     * 取消降落
     */
    @Override
    public void landCancel(LandCancelDTO landCancelDTO) {
    public Boolean landCancel(LandCancelDTO landCancelDTO) {
        LandCancelCmd landCancelCmd = new LandCancelCmd(landCancelDTO);
        this.sendMessage(tyConfiguration.getIpPort(), landCancelCmd);
        return true;
    }
    /**
     * 强制降落
     */
    @Override
    public void landForced(LandForcedDTO landForcedDTO) {
    public Boolean landForced(LandForcedDTO landForcedDTO) {
        LandForcedCmd landForcedCmd = new LandForcedCmd(landForcedDTO);
        this.sendMessage(tyConfiguration.getIpPort(), landForcedCmd);
        return true;
    }
    /**
     * 方向控制
     */
    @Override
    public void flyControl(FlyControlDTO flyControlDTO) {
    public Boolean flyControl(FlyControlDTO flyControlDTO) {
        FlyControlCmd flyControlCmd = new FlyControlCmd(flyControlDTO);
        this.sendMessage(tyConfiguration.getIpPort(), flyControlCmd);
        return true;
    }
    /**
     * 指点飞行控制
     */
    @Override
    public void flyPointControl(FlyPointControlDTO flyPointControlDTO) {
    public Boolean flyPointControl(FlyPointControlDTO flyPointControlDTO) {
        FlyPointControlCmd flyPointControlCmd = new FlyPointControlCmd(flyPointControlDTO);
        this.sendMessage(tyConfiguration.getIpPort(), flyPointControlCmd);
        return true;
    }
    /**
     * 停止指点飞行控制
     */
    @Override
    public void flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO) {
    public Boolean flyPointControlCancel(FlyPointControlCancelDTO flyPointControlCancelDTO) {
        FlyPointControlCancelCmd flyPointControlCancelCmd = new FlyPointControlCancelCmd(flyPointControlCancelDTO);
        this.sendMessage(tyConfiguration.getIpPort(), flyPointControlCancelCmd);
        return true;
    }
    /**
     * 紧急制动
     */
    @Override
    public void emeragencyBrake(EmeragencyBrakeDTO emeragencyBrakeDTO) {
    public Boolean emeragencyBrake(EmeragencyBrakeDTO emeragencyBrakeDTO) {
        EmergencyBrakeCmd emergencyBrakeCmd = new EmergencyBrakeCmd(emeragencyBrakeDTO);
        this.sendMessage(tyConfiguration.getIpPort(), emergencyBrakeCmd);
        return true;
    }
    /**
ard-work/src/main/java/com/ard/tianyu/service/impl/UavControlManager.java
New file
@@ -0,0 +1,92 @@
package com.ard.tianyu.service.impl;
import com.alibaba.fastjson2.JSON;
import com.ard.common.exception.NoPermissionException;
import com.ard.common.utils.SecurityUtils;
import com.ard.tianyu.websocket.domain.ControlRequestMessage;
import com.ard.tianyu.websocket.domain.WsMessage;
import com.ard.tianyu.websocket.utils.WebSocketUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.concurrent.ConcurrentHashMap;
@Service
@Slf4j
public class UavControlManager {
    // 存储每个无人机的当前控制者
    public static final ConcurrentHashMap<String, Long> droneControlMap = new ConcurrentHashMap<>();
    // 存储无人机的控制请求(用于判断哪个控制者正在等待)
    public static final ConcurrentHashMap<String, Long> controlRequestMap = new ConcurrentHashMap<>();
    // 发送通知(通过 WebSocket推送)
    public void notifyMessage(String boxSn, String action, Long controller, Long requester) {
        ControlRequestMessage controlRequestMessage = ControlRequestMessage.builder()
                .boxSn(boxSn)
                .requester(requester)
                .action(action)
                .build();
        WsMessage<ControlRequestMessage> wsMessage = new WsMessage<>("controlRequest", controlRequestMessage);
        WebSocketUtil.sendMessage(controller.toString(), JSON.toJSONString(wsMessage));
    }
    // 申请控制
    public boolean requestControl(String boxSn) {
        Long requester = SecurityUtils.getUserId();
        // 如果无人机没有控制者,直接控制
        if (droneControlMap.get(boxSn) == null) {
            droneControlMap.put(boxSn, requester);
            return true; // 控制成功
        }
        // 如果无人机已经有控制者,则发送申请给当前控制者
        Long controller = droneControlMap.get(boxSn);
        if(!controller.equals(requester)) {
            // 则将控制请求加入请求队列
            controlRequestMap.put(boxSn, requester);
            // 通知当前控制者,有新的控制请求
            notifyMessage(boxSn, "request", controller, requester);
        }
        return false; // 等待当前控制者的同意
    }
    // 处理控制请求的同意或拒绝
    public boolean handleControlResponse(String boxSn, boolean approve) {
        Long controller = SecurityUtils.getUserId();
        Long requester = controlRequestMap.get(boxSn);
        // 确保请求者是当前的控制者
        if (requester != null && !requester.equals(controller)) {
            if (approve) {
                // 同意控制,将控制权赋予请求者
                droneControlMap.put(boxSn, requester);
                notifyMessage(boxSn, "approve", requester, controller);
            } else {
                notifyMessage(boxSn, "reject", requester, controller);
            }
            // 无论同意还是拒绝,都移除控制请求
            controlRequestMap.remove(boxSn);
            return true;
        }
        return false;
    }
    // 释放控制
    public boolean releaseControl(String boxSn) {
        Long controller = droneControlMap.get(boxSn);
        if (controller != null) {
            if (controller.equals(SecurityUtils.getUserId())) {
                droneControlMap.remove(boxSn);
                return true;
            } else {
                // 当前控制者不匹配,可以记录日志或者抛出异常
                log.warn("无权限释放控制, 实际控制者是 {}", controller);
                throw new NoPermissionException("无权限释放控制, 实际控制者是 " + controller);
            }
        } else {
            // 说明无人机没有控制者
            log.warn("无人机当前 {} 没有控制者,无需释放", boxSn);
            throw new NoPermissionException("无人机" + boxSn + "当前没有控制者,无需释放");
        }
    }
}
ard-work/src/main/java/com/ard/tianyu/websocket/config/WebSocketConfig.java
@@ -21,7 +21,7 @@
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        //添加myWebSocketHandler消息处理对象以及websocket连接地址
        registry.addHandler(tyMessageHandler, "/ws/state/**")
        registry.addHandler(tyMessageHandler, "/ws/control/**")
                //设置允许跨域访问
                .setAllowedOrigins("*")
                //添加拦截器可实现用户链接前进行权限校验等操作
ard-work/src/main/java/com/ard/tianyu/websocket/domain/ControlRequestMessage.java
New file
@@ -0,0 +1,19 @@
package com.ard.tianyu.websocket.domain;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ControlRequestMessage {
    private String boxSn;       // 盒子编号
    private Long controller;    // 当前控制者ID
    private Long requester;     // 请求控制者ID
    private String action;      // 操作类型:request(请求),approve(同意),reject(拒绝)
}
ard-work/src/main/java/com/ard/tianyu/websocket/domain/WsMessage.java
New file
@@ -0,0 +1,13 @@
package com.ard.tianyu.websocket.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WsMessage<T> {
    private String type;  // 消息类型
    private T payload;  // 泛型消息内容
}
ard-work/src/main/java/com/ard/tianyu/websocket/handler/TyMessageHandler.java
@@ -1,6 +1,9 @@
package com.ard.tianyu.websocket.handler;
import com.ard.tianyu.websocket.domain.ControlRequestMessage;
import com.ard.tianyu.websocket.domain.WsMessage;
import com.ard.tianyu.websocket.utils.WebSocketUtil;
import com.ard.tianyu.websocket.utils.WsMessageConverter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
ard-work/src/main/java/com/ard/tianyu/websocket/handler/TyStreamHandler.java
@@ -11,6 +11,7 @@
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
ard-work/src/main/java/com/ard/tianyu/websocket/utils/WebSocketUtil.java
@@ -51,7 +51,10 @@
            }
        }
    }
    public static void sendMessage(String userId, String message) {
        WebSocketSession session = ONLINE_USER_SESSIONS.get(userId);
        sendMessage(session, message);
    }
    /**
     * @param session 用户 session
     * @param message 发送内容
ard-work/src/main/java/com/ard/tianyu/websocket/utils/WsMessageConverter.java
New file
@@ -0,0 +1,19 @@
package com.ard.tianyu.websocket.utils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.ard.tianyu.websocket.domain.WsMessage;
public class WsMessageConverter {
    // 将 JSON 字符串转换为 WsMessage 对象
    public static  <T> WsMessage<T> fromJson(String json, Class<T> payloadType) {
        // 使用 TypeReference 来处理泛型
        return JSON.parseObject(json, new TypeReference<WsMessage<T>>(){});
    }
    // 将 WsMessage 对象转换为 JSON 字符串
    public static <T> String toJson(WsMessage<T> wsMessage) {
        return JSON.toJSONString(wsMessage);
    }
}